PL. 82 · ATTRACTORS / MAP / SEARCHED, NOT DESIGNED
Sprott Quadratic Map
Julien Clinton Sprott, 1993
OPEN THE LIVE PLATE ▸DEFINITION
xₙ₊₁ = a₀ + a₁x + a₂x² + a₃xy + a₄y + a₅y² yₙ₊₁ = a₆ + a₇x + a₈x² + a₉xy + a₁₀y + a₁₁y² twelve coefficients, each a letter A–Y over −1.2 … 1.2
NOTES
Sprott’s idea was to stop designing attractors and search for them instead: pick twelve coefficients at random, iterate, test whether the orbit is bounded and its Lyapunov exponent positive, and keep the ones that pass. Hundreds of strange attractors fell out, and because each coefficient quantises to one letter, every one of them has a twelve-letter name that is also its complete definition — the code below *is* the attractor. The plate carries a curated set from the paper’s own examples; the window is measured from each orbit rather than typed, since no two of them land in the same place.
PROVENANCE
- Origin
- J. C. Sprott, "Automatic generation of strange attractors", Computers & Graphics 17(3), 1993, 325–332, and the book Strange Attractors: Creating Patterns in Chaos, 1993. The A–Y encoding maps each coefficient to −1.2 + 0.1·(letter − A)
- Standing
- Public domain — an iterated map and a search procedure
- Constants
- The code is a CHOICES list, not a slider sweep, because the space between two valid codes is not another attractor — most coefficient sets are unbounded or collapse to a point. Every code listed was put through Sprott’s own acceptance test first: bounded orbit and positive largest Lyapunov exponent. All twelve pass, from 0.036 to 0.270, each filling 11–34 per cent of its own bounding box
- Source
- doi:10.1016/0097-8493(93)90082-K
HOUDINI · VEX
The same published mathematics as a Detail Wrangle body. Paste it into a Wrangle with Run Over set to Detail; every constant is the published value plus a tweak channel, so Create Spare Parameters gives a slider that starts where the paper does.
// FORMA — PL. 82 · SPROTT QUADRATIC MAP — Julien Clinton Sprott, 1993
// xₙ₊₁ = a₀ + a₁x + a₂x² + a₃xy + a₄y + a₅y²
// yₙ₊₁ = a₆ + a₇x + a₈x² + a₉xy + a₁₀y + a₁₁y²
// twelve coefficients, each a letter A–Y over −1.2 … 1.2
// Paste into a Detail Wrangle (Run Over: Detail), no inputs needed.
// Written from the published mathematics, not adapted from any code.
// Constants arrive at their published values. Press the node's Create
// Spare Parameters button and every tweak becomes a slider — starting
// at 0, the published figure, and moving in the constant's own units.
// https://forma-gen.com/#plate=sprott
float p_code = 0 + chf('code_tweak'); // coefficient code · live 0 .. 11
// The plate's own colour: FORMA's ATTRACTORS accent as a cosine ramp,
// brightest near t = 0 and t = 1, near-black around t = 0.5.
vector forma_ramp(float t){
return set(
0.0956 + 0.1039 * cos(6.28318530718 * (t + 0)),
0.4041 + 0.4392 * cos(6.28318530718 * (t + 0.05)),
0.46 + 0.5 * cos(6.28318530718 * (t + 0.1)));
}
// Sprott's own encoding: one letter per coefficient, A–Y across −1.2 to 1.2
// in steps of 0.1. Decoding the letters here rather than storing numbers
// keeps the constant and the paper's name for each attractor as the same
// thing. All twelve passed Sprott's acceptance test — bounded orbit,
// positive largest Lyapunov exponent — before they were listed.
int forma_pts = 120000;
int forma_skip = 20;
string forma_codes[] = {
"GLXOESFTTPSV", "MCRBIPOPHTBN", "VBWNBDELYHUL", "FIRCDERRPVLD",
"QFFVSLMJJCCR", "LUFBBFISGJYS", "EJYDREGLYQPV", "HIYIWHOKNVCG",
"MSSSRRPADDSO", "OIHVGHAHGYRK", "RALLTIOBDULT", "WJJFXGXHTRPG" };
string code = forma_codes[int(p_code) % 12];
float A[];
for (int i = 0; i < 12; i++)
push(A, float(ord(code[i]) - 65) * 0.1 - 1.2);
float x = 0.05, y = 0.05;
for (int i = 0; i < forma_pts + forma_skip; i++){
float nx = A[0] + A[1] * x + A[2] * x * x + A[3] * x * y + A[4] * y + A[5] * y * y;
float ny = A[6] + A[7] * x + A[8] * x * x + A[9] * x * y + A[10] * y + A[11] * y * y;
// a jittered code's escape goes back to the start, not into the cloud
if (!isfinite(nx) || !isfinite(ny) || abs(nx) > 1e5 || abs(ny) > 1e5){
x = 0.05; y = 0.05; continue;
}
x = nx; y = ny;
if (i < forma_skip) continue;
// canvas y runs down; negated so the figure sits as the plate shows it
int pt = addpoint(0, set(x, -y, 0.0));
float u = float(i) / float(forma_pts + forma_skip);
setpointattrib(0, "Cd", pt, forma_ramp(0.8 + 0.3 * u));
}