Field notes · site 155 · Wave 7 Chance
How a whole grid learns to connect at once
Percolate takes one stochastic law — site percolation on a square lattice — and makes the phase transition physical. Open each cell with probability p, cluster what touches what, and raise p. Below pₒ ≈ 0.5927 the field is all islands; cross it and a single cluster leaps edge to edge. This page is how it is built.
01 The concept
One number decides whether the field is connected
Percolation is the cleanest phase transition in probability. Fill a large grid at random — each cell open with probability p, independently — and ask a single question: is there a path of open, touching cells all the way across? For small p the answer is plainly no. For large p, plainly yes. The surprise is that the switch is sharp: on the infinite square lattice there is a single critical value pₒ ≈ 0.5927 with only finite islands below it and an infinite spanning cluster above it.
The fictional brief: a single, wordless instrument for a curious reader who has heard the phrase "phase transition" and wants to feel one. Its only job is to let you watch connection arrive — not fade in, arrive — as the threshold is crossed.
02 Palette
Two states, one edge between them
Teal is a cell that is open and connected; a warm flare is the cluster that has won its way across. Everything else is unopened ground. The bright marks are reserved for strokes and cells — the body ink is a separate, calmer grey derived to clear 4.5:1 on the dark field.
03 Type
A geometric display, a technical monospace
04 Technique
Ten thousand instanced tiles, clustered every frame
- three.js
InstancedMesh— a 100 × 100 grid of tiles (10,000 instances) laid as a shallow slab. Each cell carries a frozen occupation threshold and a smooth relief height as per-instance attributes. - Custom
ShaderMaterialvertex displacement — openness is decided on the GPU fromaRand < uP, so raising p opens cells smoothly for free every frame. The vertex shader lifts open cells to their relief height and spanning cells higher still. - Union-find clustering — weighted union with path compression labels every open cell into clusters, then tests whether one root reaches both the left and right columns. Recomputed only when p moves past a small delta, and the spanning membership is uploaded as a single dynamic attribute.
EffectComposer+UnrealBloomPass— a high threshold so only the ignited spanning cluster blooms, keeping the teal islands crisp.- Threshold sweep — p rests in a spanning hold, then breathes across pₒ so the ignition recurs; a drag/keyboard rail lets you scrub the threshold yourself and feel the jump.
The signature. Below pₒ the largest cluster is a small fraction and the cluster count runs into the hundreds. As the sweep lifts p through 0.5927, dozens of islands fuse in a few frames: the count collapses toward one, the largest cluster leaps to fill most of the lattice, and that winning cluster recolours from teal to a blooming flare the instant it touches both edges. Nothing eases — it arrives.
05 Iteration log
Three passes, driving the transition each time
Rebuilt the hero scrim as a directional multi-stop gradient so the monospace lede clears the bright slab, and narrowed that column to 30rem so its right edge never runs into the flare. Tamed the bloom (strength 0.72 → 0.52, threshold 0.66 → 0.74) so the teal islands read against the orange span instead of washing out, and set the closed / open / spanning colours in linear space so the rendered hexes match the palette.
Gave the spanning cluster a slow per-cell breathing in the vertex shader, and wired the live readouts so the cluster count visibly collapses from hundreds to ~one while the largest-cluster percentage leaps — a second read the data performs on its own at pₒ. Added the drag-and-keyboard threshold rail and an ignition flash the first time a field spans, so the transition can be driven by hand, not only watched.
Verified 375px (wordmark and nav within the viewport, scrollWidth clean) and added a mobile
top-scrim so the hero and readouts stay legible over the slab. Confirmed reduced-motion renders
a single static spanning frame with the loop parked, and added a spanning hold window so the
static thumbnail is reliably mid-span regardless of load timing. Capped DPR at 2 across
renderer, composer and bloom, paused the loop on document.hidden, wired a
WebGL context-loss fallback, and removed a redundant vignette layer.