Strange Attractors Revisited

A few years ago I wrote a post about rendering Clifford strange attractors in C++ with OpenGL. The code has been sitting on GitHub ever since, and recently I decided to dust it off and extend it — with a lot of help from Claude Code.

What is a strange attractor, briefly

A strange attractor is a set that a chaotic dynamical system orbits around without ever quite repeating itself. The system is locally unstable — tiny changes in starting position blow up — yet globally it stays confined to this fractal structure. The result looks almost organic: dense, intricate, endlessly detailed.

The classic example is the Lorenz system, shaped like a butterfly. The attractors I render here are discrete-time — instead of solving differential equations, you just iterate a pair of equations and plot the resulting cloud of points.

What changed

The original code only rendered the Clifford attractor. The new version adds four more:

AttractorCharacter
King’s DreamDense, symmetric, often floral or crystalline
CliffordSmooth lobes, great at broader shapes
De JongCompact, knotted structures
BedheadChaotic tendrils
SvenssonLayered, almost architectural

Coloring is now configurable too. The animations below use hue_cycle mode, which walks through hues as the iteration count increases, giving the depth cues you see in the denser regions. There are also velocity (color by how fast the point moves) and angle (color by direction) modes.

The renderer was also extended with interactive exploration — launch with --interactive and you can tweak the four parameters a, b, c, d live with keyboard controls, cycle color modes, and save individual frames.

The animations

These are all looping GIFs generated by sweeping a circular path through the attractor’s parameter space. Each frame is a full render with 2 million plotted points. I cherry-picked the ones where the motion felt most alive.

How the parameter sweep works

The animation loop is generated by walking a circle in the four-dimensional parameter space (a,b,c,d)(a, b, c, d):

a(t)=a0+δcos(2πt),b(t)=b0+δsin(2πt)a(t) = a_0 + \delta \cos(2\pi t), \quad b(t) = b_0 + \delta \sin(2\pi t) c(t)=c0+δcos(2πt+ϕ),d(t)=d0+δsin(2πt+ϕ)c(t) = c_0 + \delta \cos(2\pi t + \phi), \quad d(t) = d_0 + \delta \sin(2\pi t + \phi)

where δ\delta controls the amplitude of the sweep and ϕ\phi adds a phase offset so cc and dd don’t track aa and bb exactly. Because it is a closed loop in parameter space, the animation loops seamlessly.

The key insight from the original post still holds: the attractor shape is continuous in its parameters. Walk a small enough circle and you get smooth, hypnotic motion. Walk a larger circle and the structure can collapse or explode — so picking δ\delta well matters.

Running it yourself

git clone https://github.com/gumeo/movingAttractor
cd movingAttractor
make
./attractor --help

To generate your own animation:

mkdir frames
./attractor --type kings_dream --color hue_cycle \
  --save --output frames --frames 120 \
  --width 800 --height 800

ffmpeg -r 24 -i frames/%05d.ppm \
  -c:v libx264 -pix_fmt yuv420p -crf 18 output.mp4

Or just launch interactive mode and play:

./attractor --interactive --type clifford --color hue_cycle

Keys Q/q W/w E/e R/r nudge parameters up and down live, c cycles the color mode, Space pauses.

Reflections

The original post was about learning how to use OpenGL to render millions of points efficiently. That part hasn’t changed — the rendering core is the same. What’s new is the parameter exploration infrastructure: multiple attractor families, color strategies, and a decent CLI so you can script batch runs without recompiling.

I find the King’s Dream attractor particularly compelling. The filament variant has this quality where the structure feels almost biological — like looking at a cross-section of something that grew rather than something that was designed.

Strange attractors occupy a nice corner of mathematics: deterministic, but endlessly surprising.