Attractors: Finding Order Inside Chaos

An instrument for finding and drawing strange attractors: 51 chaotic systems rendered live on the GPU, a search that hunts for new ones, and an editor for your own equations. I first built a version of this in 2014 as attractors.io, a JavaScript page built on three.js and web workers. This is a ground-up rebuild, and it's live at attractors.charliedeck.com.

What a Strange Attractor Is
Take a point , push it through a pair of equations, and feed the result back in. Here is the map behind the image above, Clifford Pickover's:
Plot every point the orbit visits and one of four things happens. The point can fly off to infinity, collapse onto a single fixed point, settle into a loop that repeats, or, for the parameters worth looking at, wander forever without repeating and without leaving a bounded region. That last case is a strange attractor. Every orbit that starts nearby gets drawn onto the same shape, which is the "attractor" part. The "strange" part is that the shape is fractal and the motion on it is chaotic: two orbits that start a hair apart separate exponentially fast, so where any one point lands is unpredictable even though the overall picture is perfectly stable.
That combination is what makes the images. A single orbit, iterated a few hundred million times, traces the attractor's full density, and the density is the picture: bright where the orbit lingers, dark where it rarely goes.
The library has two kinds of system. Maps like Clifford's jump from point to point. Flows like the Lorenz system, the 1963 weather model that gave chaos its butterfly, are differential equations that move a point continuously; the app steps them with a numerical integrator and draws the threads they leave.

Fifty-One Systems, One Small Language
In the 2014 version each attractor was a JavaScript function. The rebuild treats systems as data. Each one is a few lines in a small equation language: Clifford's two lines above are exactly what the catalog stores, along with parameter ranges, named presets and a one-sentence description. A compiler turns those lines into two programs, WGSL for the GPU renderer and JavaScript for everything that runs on the CPU, so the picture and the measurements always come from the same equations.
The same compiler is behind the equation editor, so you can type your own system and draw it immediately. Your text never reaches either target verbatim: the compiler parses it into a syntax tree and emits code only from sanitized names, numbers and a fixed list of known functions.
The catalog spans the classics: trigonometric maps (Clifford, Peter de Jong, Bedhead), polynomial maps (Hénon, the Sprott quadratics), Field and Golubitsky's symmetric icons from Symmetry in Chaos, 3D maps, and the flows (Lorenz, Rössler, Aizawa, Thomas, Halvorsen, Chen, Rabinovich–Fabrikant and more).
Hundreds of Millions of Points a Second
The renderer seeds a large batch of particles on the attractor and iterates them in a compute shader. Every step, each particle adds one to the pixel it lands in, using atomic adds into a shared density histogram. On a recent GPU that runs to hundreds of millions of points a second, and the image keeps sharpening the longer you watch.
The raw counts span many orders of magnitude, from pixels visited once to pixels visited millions of times, so the tone curve is logarithmic: brightness grows with . Full brightness is pinned to the level that only the brightest 0.2% of lit pixels exceed, rather than to the maximum, so one hot pixel at a fixed point or cusp can't dim the whole picture. Browsers without WebGPU get a close approximation from a CPU renderer running in a worker, which shares the same tone curve.
Telling Chaos from Order
A random parameter setting usually doesn't produce anything worth drawing. Most points escape or collapse, and many of the survivors are tidy loops. So before anything reaches the screen, the CPU measures it.
The key number is the largest Lyapunov exponent, : how fast nearby orbits separate. The app runs a second "shadow" orbit a tiny distance from the first, steps both, measures their new separation , adds to a running total, then pulls the shadow back to distance along the same direction and repeats. The average of those logarithms is . If it's positive, errors grow exponentially and the orbit is chaotic; at or below zero it is ordered. The Clifford map above measures per iteration.
The second number is the correlation dimension, , which says how much of space the attractor fills. Count the fraction of point pairs closer than a radius ; for a fractal that fraction grows like , so the slope of against estimates (the Grassberger–Procaccia method, taken here between 1% and 10% of the attractor's extent). A smooth curve scores about 1 and a filled region about 2; Clifford's sits at 1.55, a shape thicker than a line and thinner than a sheet.
Searching and Breeding
Those measurements are what make search possible. Generate draws candidates, runs each through the analysis on a pool of workers, and keeps only those that stay bounded, pass the chosen filter (chaotic, ordered or either), and aren't a thin curve. Search can look in the current system, near the current parameters, or anywhere in the library.
The nearby search treats parameters like genes. A temperature from 0 to 1 sets how far each step reaches, from a hair's breadth to about a fifth of each parameter's range, so a batch fans out from close variations of the parent to distant ones. Every result keeps its line of ancestry, and two results of the same system can be crossed into a child that mixes their parameters. You can lock a parameter to hold it fixed while the rest vary.
For steering by hand there's the atlas: pick two parameters and the app colors a grid by the Lyapunov exponent at each point, so you can see where the chaotic islands are before you drag into them. Beyond that, there's a kaleidoscope that folds the image into symmetric copies, parameter drift that animates the attractor as its equations slowly change, clip recording, and poster export.
attractors.charliedeck.com: press Space to generate a batch, or A to jump anywhere in the library.