fernstack

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:

RuleabcdefChanceDraws
1stem0000.16001%stem
2copy of the frond0.850.04−0.040.8501.6085%copy of the frond
3left leaflet0.20−0.260.230.2201.607%left leaflet
4right leaflet−0.150.280.260.2400.447%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

0.04
−0.04
0.85

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.

Back to the stack