How the fern on this page grows
The orange fern on the start page and the one further down this page are not image files. Your browser draws them, one point at a time, from four short rules and a random number. This note explains those rules, shows the code, and lets you move the numbers yourself.
Four rules and an uneven coin
The shape is the Barnsley fern, an iterated function system described by Michael Barnsley in his 1988 book Fractals Everywhere. It is made of four affine maps. Each one takes a point and moves it to a new place:
new x = a * x + b * y + e
new y = c * x + d * y + f
The six numbers are different for every rule, and every rule has its own chance of being picked:
| Rule | a | b | c | d | e | f | Chance | Draws |
|---|---|---|---|---|---|---|---|---|
| 1stem | 0 | 0 | 0 | 0.16 | 0 | 0 | 1% | stem |
| 2copy of the frond | 0.85 | 0.04 | −0.04 | 0.85 | 0 | 1.60 | 85% | copy of the frond |
| 3left leaflet | 0.20 | −0.26 | 0.23 | 0.22 | 0 | 1.60 | 7% | left leaflet |
| 4right leaflet | −0.15 | 0.28 | 0.26 | 0.24 | 0 | 0.44 | 7% | right leaflet |
Rule 2 carries most of the work. It shrinks the whole fern a little, tilts it and lifts it up the stem, which is why every leaflet looks like a small copy of the entire plant. Rule 1 squashes everything onto a thin vertical line, so it only needs one point in a hundred to draw the stem.
The chaos game
Start at the origin. Pick a rule at random with the chances above, apply it and mark the new point. Repeat. After a handful of steps the point lands on the fern and never leaves it, so every mark you make belongs to the picture. Enough marks fill it in.
Open a blank tab, open the browser console and paste this. It draws the same fern in a fraction of a second:
const canvas = document.body.appendChild(document.createElement("canvas"));
canvas.width = 480;
canvas.height = 980;
const ctx = canvas.getContext("2d");
ctx.fillStyle = "rgba(17, 17, 17, 0.35)";
let x = 0;
let y = 0;
for (let i = 0; i < 140000; i++) {
const r = Math.random();
const [a, b, c, d, e, f] =
r < 0.01 ? [0, 0, 0, 0.16, 0, 0] :
r < 0.86 ? [0.85, 0.04, -0.04, 0.85, 0, 1.6] :
r < 0.93 ? [0.2, -0.26, 0.23, 0.22, 0, 1.6] :
[-0.15, 0.28, 0.26, 0.24, 0, 0.44];
[x, y] = [a * x + b * y + e, c * x + d * y + f];
ctx.fillRect(220 + x * 90, 960 - y * 95, 1, 1);
}
Move the numbers
Rule 2 decides the shape of everything above the first leaflets, so small changes to it reshape the whole plant. The start page shows twelve fixed steps; here you can set b, c and d to anything in between.
Rule 2: x′ = 0.85·x + b·y, y′ = c·x + d·y + 1.6
These are the original numbers.
Growing it from the root
Plain chaos game fills the fern everywhere at once, like a photo developing. To make it grow, the start page works in two passes:
- It generates all 140,000 points first and sorts them into 240 bands by height with a counting sort, which takes a few milliseconds.
- It then draws the bands from the root up. Inside a band the order stays random, so the growing edge looks soft instead of a ruler line.
- The drawing speed follows a cubic ease out over 3.6 seconds: quick at the start, slowing down as it reaches the tip.
Why the stem is the darkest part
Each point is a dot of ink at 60% opacity. Where many points land on the same pixel they stack up, so the stem and the midribs of the leaflets turn solid black while the tips stay grey. There is no shading in the code, only overlap.
Bending without flicker
The random numbers come from a seeded generator, so every drawing of the fern visits the same sequence of rules. When you point at a cell on the start page and b moves from 0.04 to 0.12, each point slides a little instead of the whole cloud reshuffling, and the fern visibly bends.
Keeping it light
- The script is under 500 lines and has no dependencies. You can read all of it at /assets/js/fern.js.
- Growth pauses when the fern scrolls out of view and picks up where it stopped.
- If your system asks for reduced motion, the fern appears fully grown and changes without easing.
- The canvas follows your screen density up to 2x, sharp on most displays without wasting memory on very dense ones.
Changelog
- First version.