Iterated function systems

A rule that makes structure out of noise

Pick a polygon. Start anywhere. Roll a die, jump partway toward that vertex, mark the spot, repeat forever. On a square this fills in a dull grey blob. Then forbid one thing — never pick the same vertex twice in a row — and a Sierpiński-like fractal appears out of the same randomness. Everything below is computed live in your browser.

n = 4 · r = 0.50
Points plotted 0
Moves allowed 3 of 4
Fractal dimension
predicted1.585 measured
Box-counting the plate…
Presets
Vertices4
4
Jump fraction r0.500
contraction 1 − r0.500
Allowed moves

Each button is a step relative to the vertex you just used. Switch one off to forbid that move.


What the machine is doing

The chaos game is three lines long, and that is the point — no fractal is designed, drawn, or subdivided anywhere in it. The structure is a side effect of which moves you refuse to make.

  1. Drop a pointAnywhere inside the polygon. After a few dozen steps it forgets where it started, so the plate discards the first 30.
  2. Choose a vertexUniformly at random — but only from the moves still switched on in the rail above.
  3. Jump and markMove the fraction r of the way there, ink the spot, repeat. Hue records which vertex was chosen.

Why forbidding a move changes the dimension

Each jump is a contraction: it shrinks the whole plane by a factor of 1 − r toward the chosen vertex. With every move allowed, the attractor is n shrunken copies of itself. Forbid some moves and it becomes A copies instead, where A is the number of offsets left switched on. Because the rule is written as offsets, it applies identically at every vertex — the transition matrix is circulant, so its Perron eigenvalue is exactly A, and the similarity dimension collapses to something you can read straight off the controls:

D = log A ⁄ log ( 1 ⁄ (1 − r) )

A square with every move allowed gives log 4 / log 2 = 2 — a solid blob, exactly what you see. Ban the repeat and A drops to 3: log 3 / log 2 = 1.585, the Sierpiński triangle's dimension, on a square. Nothing about the geometry changed. One outlawed move removed half a dimension.

The formula assumes the A copies don't overlap. When it returns more than 2 they must — the pieces collide, the excess is wasted, and the measured value falls below the prediction instead of matching it. The plate flags that case as it happens.


Checking it against measurement

Before building this page I ran the same simulation offline: 6 million points per configuration onto a 1024 × 1024 grid, box-counted across five scales, dimension read off the log–log slope. Prediction against measurement:

Box-counting dimension · 6 000 000 points · grid 1024²
PolygonrRuleAPredictedMeasuredΔ
Triangle0.500all moves31.5851.597+0.012
Square0.500all moves42.0001.975−0.025
Square0.500no repeat31.5851.594+0.009
Square0.500no neighbour21.0000.999−0.001
Square0.500no opposite31.5851.580−0.005
Pentagon0.618all moves51.6721.668−0.004
Pentagon0.618no repeat41.4401.430−0.011
Pentagon0.500no repeat42.0001.827copies overlap
Hexagon0.667all moves61.6311.619−0.012
Hexagon0.667no repeat51.4651.436−0.029
Hexagon0.667no neighbour41.2621.267+0.005
Heptagon0.692all moves71.6521.649−0.003
Heptagon0.692no repeat61.5211.525+0.003

Twelve of the thirteen land within 0.03 of the prediction — ordinary box-counting bias at finite resolution. The thirteenth misses by 0.17, and it is precisely the row where the formula returns 2.0 for a set whose four copies are known to overlap. The one configuration that breaks the rule is the one the rule says it cannot describe.

A fourteenth, found by playing

Ban both neighbours on a square and the orbit is trapped between two opposite vertices — the attractor is a straight vertical line, dimension exactly 1. For a long time this plate read about 0.84 for it, roughly half the times you loaded it, and blamed the gap on convergence or overlapping copies. Both were wrong.

cos(π/2) is 6.1 × 10⁻¹⁷, not 0, so the line's true x sits a hair right of centre. A run that happens to start on the negative side crosses zero about fifty steps in, and the few points plotted before it does land one pixel column to the left. That column falls on the plate's exact midpoint, so no halving ever merges the two — the strays add a constant to the count at every scale, and a constant on top of a halving sequence bends the fitted slope down. Burning the transient to below float noise fixes it; the burn-in is now sized from r rather than fixed at 30 steps.

Worth keeping in mind while reading any measured column on this page: the prediction was right, the mathematics was right, and the instrument was quietly wrong in a way that looked like physics.