Barnsley Fern
Four rules, picked at random, grow a leaf. Put a point at (0, 0), roll a die weighted 1 : 85 : 7 : 7, move the point by the affine map the roll picks, mark where it lands, and repeat. Every mark falls on the same fern, the one Michael Barnsley printed as four rows of numbers in 1988. The plate grows it live as density, draws each of the four maps as a frame over it, and the slider under it turns one map so the whole frond curls.
About this piece
The Barnsley fern is drawn by a game of chance. Put a point at (0, 0). Pick one of four affine maps at random, the first with probability 1%, the second 85%, the third and fourth 7% each. Move the point with that map, mark where it lands, and do it again. After a few rolls every mark lies on the fern. The order the marks arrive in is random, but the picture they fill is not. This plate plays that game live, at about five points per device pixel of the fern’s rectangle (about 800,000 in a 1280-pixel-wide window at normal pixel density, up to a cap of 2.4 million on high-density screens). Each pixel is coloured by how often it was hit, on a log scale along the theme’s ramp: background, then deep cobalt, cobalt, and finally coral where the hits pile up most.
The recipe is drawn on top. The dashed rectangle is the box around the whole fern, and the four solid frames show where each map sends that box: f1 in the text ink, f2 in cobalt, f3 in coral and f4 in deep cobalt. The readout in the bottom-left corner (on screens wide enough to fit it beside the fern) lists each map’s probability next to how often it has actually been picked so far. A fern grows over 7 seconds and is held for 9. Then it grows again from fresh dice, so the shape stays the same and only the grain changes. The stage opens with 40% of the points already in. With reduced motion switched on you get the grown fern as one still frame. The slider still works and redraws it, and nothing animates.
The four maps, as Barnsley printed them
Each map is x′ = a·x + b·y + e, y′ = c·x + d·y + f: a linear squeeze, turn or flip given by a–d, followed by a shift (e, f). These are the four rows the plate uses, digit for digit:
a b c d e f p draws f1 0.00 0.00 0.00 0.16 0 0.00 0.01 the stem f2 0.85 0.04 −0.04 0.85 0 1.60 0.85 successively smaller leaflets f3 0.20 −0.26 0.23 0.22 0 1.60 0.07 largest left-hand leaflet f4 −0.15 0.28 0.26 0.24 0 0.44 0.07 largest right-hand leaflet starting at (0, 0), every point stays inside −2.1820 < x < 2.6558, 0 ≤ y < 9.9983
Read as geometry: f1 flattens the whole fern onto the segment x = 0, 0 ≤ y ≤ 1.6, which is the stem. f2 shrinks the fern to 0.85 of its size, turns it 2.7° clockwise and lifts it 1.6 up the stem, so it lands on the fern minus its bottom pair of leaflets. f3 shrinks it to about a third and turns it about 49° anticlockwise, making the lowest left leaflet. f4 does much the same on the right, but its determinant is negative (−0.109), so that leaflet is a mirror image of the fern.
Michael Barnsley, a British mathematician, published this table as table III.3, “IFS code for a fern”, in his 1988 book Fractals Everywhere (Academic Press). The book grew out of the Fractal Geometry course he taught at the Georgia Institute of Technology, and he shaped the fern after the black spleenwort, Asplenium adiantum-nigrum. The coefficients, the probabilities and the part each map draws are taken from Wikipedia’s article “Barnsley fern”, which reproduces that table and cites the book. The bounding box is the one quoted in Rosetta Code’s “Barnsley fern” task, whose Go entry takes it from an earlier revision of the same Wikipedia article. Both were read on 12 October 2026. The test suite checks the plate’s maps and probabilities against this table, and runs a million seeded points to confirm none of them leaves the box.
Why random choices draw a definite leaf
Every map pulls points closer together: f2 by a factor of 0.85, f3 and f4 to about a third, and f1 all the way down to a line. For any set of shrinking maps like this there is exactly one shape that is the union of its own images under them. That shape is called the attractor, and here it is the fern: four smaller copies of itself, one per frame on the plate. A point moved by randomly chosen maps falls onto the attractor within a few steps and never leaves it. Barnsley’s collage theorem works in the other direction: cover a shape with shrunken copies of itself, read off the maps, and the chaos game gives back something close to the shape. That is how the table was found.
The probabilities do not decide the shape. They decide where the points pile up, and so how fast each part fills in. A common rule of thumb weights each map by how much it shrinks area, which is the size of its determinant: 0.724 for f2, 0.104 for f3 and 0.109 for f4. That would give f2 about 77%, f3 11% and f4 12%. Barnsley’s table gives f2 more, 85%, so the upper frond and its tip fill in quickly while the two lowest leaflets stay sparse for longer. You can see this on the plate. f1 shrinks area to nothing, yet it still needs a small chance: a map that is never picked adds nothing, and without f1 the stem all but disappears (in a test run of two million points, the hits on the stem fell from 16,040 to 94).
The tip is special too. The one point f2 leaves where it is, its fixed point, is (2.6556, 9.9585), the tip of the fern. Applying f2 again and again walks any point up the frond towards it, so the tip is the busiest pixel on the plate and the first to turn coral. Its x is also, to four decimals, the right-hand edge of the bounding box.
Bend it: what the slider changes
The slider under the plate turns f2’s whole output, shift included, by up to 6.9° either way about the foot of the stem, and leaves f1, f3 and f4 exactly as printed. f2 is the map that stacks the fern on a smaller copy of itself, so a turn in f2 compounds. The frond leans by that angle, the next copy up leans by it again, and so on up to the tip, so a few degrees curl the whole leaf. Barnsley’s f2 already turns 2.7° clockwise, which is why his fern leans right. Add 2.7° anticlockwise and f2 becomes a pure shrink-and-lift: the frond grows straight up. Go further and it curls left. At the right-hand end the frond rolls into a hook about 7.9 units wide and 8.9 tall, compared with 4.8 by 10.0 for Barnsley’s own. At the left-hand end it bends right into a crook about 7.4 by 6.9. The frame is refitted to each bend so the whole fern stays on screen, and the readout states the angle.
Reading the plate: what to watch for
- Every leaflet is a fern. The f3 and f4 frames each hold a complete small fern. Each leaflet of those is again a fern, and so on down. On a laptop stage you can follow it about four levels before the leaflets are only a few pixels across.
- The readout settles on the table. After a million picks, the 85% map is typically within 0.04 percentage points of 85.00%. That is one standard deviation, √(0.85 × 0.15 / 106). The test suite allows 0.5 points, and the plate’s own generator lands far inside that.
- The density is uneven, on purpose. The frond’s centre line and the tip are bright, while the lowest leaflets are faint and grainy. That is the 85 : 7 : 7 weighting showing through, not missing detail.
- The frames nest. The f2 frame almost fills the dashed box, shifted up by the length of the stem and tilted 2.7°. f1’s frame has no width, so it shows as a short line along the bottom of the stem.
How it is computed
The plate keeps one 32-bit counter per device pixel of the fern’s rectangle. The dice come from a small seeded generator (mulberry32), so the gallery tile and the embed grow the same fern from the same seed. Each frame adds points in proportion to the wall-clock time since the last one (the total divided by 7 seconds, with frame gaps clamped at 0.1 s), so a 30 Hz screen and a 120 Hz one grow at the same speed. Counts become colours through a 256-step table, with u placed on the ramp at 0 for the background, 0.5 for the deep cobalt, 0.86 for the cobalt and 1 for the coral:
u = log(1 + hits) / log(1 + 20 × average hits per lit pixel)
Only the fern’s rectangle is written to the canvas, in one putImageData call. The frames are ordinary strokes on top, each with a background-coloured halo so it reads over the leaves. A theme change repaints the same counts in the other inks, and the canvas is only redrawn when points were added.
Honest limits
A caricature of a plant. A real black spleenwort is not exactly self-similar and stops branching after a few levels. This fern repeats forever, and every leaflet is a perfect mirror of every other at its level.
The colours measure the dice, not the fern. Brightness shows how often the chaos game visits a pixel, which comes from the 1 : 85 : 7 : 7 weights. Change the weights and the same fern would be lit differently.
Pixels, not infinity. At five points per pixel the faint lowest leaflets stay grainy, and nothing smaller than a pixel is ever seen. Very large screens are capped at 2.4 million points, so they are grainier still.
The slider is one map and this page only. It turns f2 alone, and it reaches the stage through postMessage from this page. An embed on another site shows Barnsley’s fern unbent. The bounding box and the frequency checks in the test suite apply to Barnsley’s maps, not to bent ones.
A 30-line version
The snippet below grows a still fern from a million points in 30 lines of HTML and plain JavaScript, with no library. It uses the same table and the same log-density colouring, in one ink. The test suite runs it against a stub canvas and checks that its maps are the plate’s and that it marks pixels only inside the fern’s box. It leaves out the frames, the readout, the growing animation, the bend, sharp drawing on high-density screens (it draws in CSS pixels) and the reaction to theme changes. Because nothing moves, there is no reduced-motion case to handle.
Things worth changing: set f2’s b and c to 0 and the frond stands up straight. Set f1’s p to 0 and give its 0.01 to f2: the stem all but vanishes. Swap the signs of f4’s a and c and its leaflet stops being a mirror image.
<canvas id="fern" style="position:fixed; inset:0"></canvas>
<script>
const dark = matchMedia('(prefers-color-scheme: dark)').matches;
const [ground, ink] = dark ? [[11, 13, 18], [150, 180, 255]] : [[231, 226, 213], [40, 72, 205]]; // the site's cobalt
// Barnsley's four maps: x' = a x + b y + e, y' = c x + d y + f, each picked with probability p.
const MAPS = [ // a b c d e f p
[ 0.00, 0.00, 0.00, 0.16, 0, 0.00, 0.01], // f1 the stem
[ 0.85, 0.04, -0.04, 0.85, 0, 1.60, 0.85], // f2 the whole fern, smaller and turned
[ 0.20, -0.26, 0.23, 0.22, 0, 1.60, 0.07], // f3 the largest left-hand leaflet
[-0.15, 0.28, 0.26, 0.24, 0, 0.44, 0.07], // f4 the largest right-hand leaflet
];
const cv = document.getElementById('fern'), ctx = cv.getContext('2d');
const W = cv.width = innerWidth, H = cv.height = innerHeight;
const s = Math.min(H / 10.6, W / 5.2), ox = W / 2 - 0.24 * s; // the fern is 10 tall, 4.84 wide, centred on x = 0.24
const hits = new Uint32Array(W * H); let x = 0, y = 0;
for (let i = 0; i < 1e6; i++) {
let r = Math.random(), k = 0;
while (k < 3 && r >= MAPS[k][6]) r -= MAPS[k++][6]; // walk the probabilities until r falls inside one
const [a, b, c, d, e, f] = MAPS[k];
[x, y] = [a * x + b * y + e, c * x + d * y + f];
const u = Math.floor(ox + x * s), v = Math.floor(H - 0.3 * s - y * s);
if (u >= 0 && u < W && v >= 0 && v < H) hits[v * W + u]++;
}
const img = ctx.createImageData(W, H);
for (let p = 0; p < W * H; p++) {
const t = Math.min(1, Math.log1p(hits[p]) / Math.log(40)); // log density, 0 = empty, 1 = 39 hits or more
for (let ch = 0; ch < 4; ch++) img.data[4 * p + ch] = ch === 3 ? 255 : ground[ch] + (ink[ch] - ground[ch]) * t;
}
ctx.putImageData(img, 0, 0);
</script>
Curious how the loop and canvas fit together? Read how it works →
More from the gallery
- Plate 01 Quantum Fibonacci Golden-angle phyllotaxis blooming from the seed outward.
- Plate 02 Bitcoin Matrix A quiet rain of ₿ and hex sliding down the glass.
- Plate 03 Cosmic Circles Soft orbs drifting through slow orbital rounds.
- Plate 04 Spiral Waves Archimedean arms turning, so the bands seem to stream inward.
- Plate 05 Particle Burst A continuous bloom of particles from the core.
- Plate 06 Geometric Flow Hexagons orbiting in harmonic rotation.