Ising · Field Notes

How it was made

Ising — a phase transition made of chance

A quarter-million spins, each flipping a coin against its four neighbours under the Metropolis rule. This page is the field notebook: the physics, the shader, the palette, and the three passes that took it from working to finished.

The concept

Ising is a fictional demonstration bench for the two-dimensional Ising model — the simplest system that shows a genuine phase transition. Its single job: let anyone feel the critical point by dragging a temperature slider, and watch a sheet of magnetic spins decide, moment to moment, whether to be a magnet or a fog of noise.

Below the critical temperature Tc = 2.269 one colour conquers and the lattice locks into a magnet. Above it, thermal chance shreds every pattern into salt-and-pepper. And exactly at Tc the domains grow to every size at once — critical opalescence — while the net magnetization collapses through zero. None of this is scripted; it emerges from millions of independent biased coin-flips.

Palette — argued from the physics

Ground
#120A14
A near-black plum. Cool enough to read as laboratory dark, warm enough that both spin colours sit on it without clashing.
Up-spin · line/mark
#C25A8A
Rose-magenta for the +1 domains. A mark colour only — never body text.
Down-spin · line/mark
#3A6EC0
Cobalt for the −1 domains. The two spins are opposites in hue as well as sign.
Ash-ink
#E0D6DC
The body ink, derived light so it clears 4.5:1 on the ground. The dark accents stay for strokes.
Up-ink (derived)
#E79FBE
A lightened magenta for readouts and eyebrows tied to the up-spin, kept legible.
Down-ink (derived)
#8FB1E4
The legible cousin of the cobalt, for the disordered-phase label and negative magnetization.

The bright spin colours are reserved for the lattice and its marks. Every string of copy uses ash-ink or one of the two derived light inks — the split that keeps small text readable over a shimmering field.

Type

Ising
2.269

Space Grotesk carries the display voice — a grotesque with just enough quirk in its terminals to feel like an instrument panel rather than a corporate sans. Space Mono runs every number: temperature, magnetization, the axis labels. Numbers are the whole story here, so they get a monospace with real character. The pairing shares a designer and a skeleton, so the two faces feel like one system tuned differently.

Technique — the signature

The lattice is a WebGL2 ping-pong simulation. Spins live in the red channel of an RGBA8 texture (255 = up, 0 = down) — no float-texture extension needed, so it runs almost everywhere. Each frame renders a full-screen quad into the opposite framebuffer, running the Metropolis rule in the fragment shader: read the four neighbours (periodic, via REPEAT wrap), compute the energy change ΔE = 2·s·Σneighbours, and flip the spin if ΔE ≤ 0 or a hashed random draw beats exp(−ΔE/T).

Neighbours can't all update at once without racing, so each sub-step updates only one colour of a checkerboard — parity 0 then 1 — the standard trick for a correct parallel Ising. Twelve sub-steps run per frame for a lively boil. The net magnetization is read back with gl.readPixels every eighth frame and plotted against the exact Onsager order-parameter curve, so the live dot rides a known analytic line straight through zero at Tc. The display shader lightly smooths the domains and adds a faint glow along domain walls, which is what makes the critical shimmer read as opalescence rather than noise.

Iteration log

Pass 1 · Craft
  • Placed the Tc 2.269 tick as a computed percentage on the slider track so it lands exactly on the critical fraction, not an eyeballed guess.
  • Rebalanced the type scale: hero brand to a true display size with negative tracking; demoted the lede to a calmer measure (max 42ch) so it stops competing with the wordmark.
  • Swapped body copy off the bright spin accents onto ash-ink and the two derived light inks — the ink/line split — after checking contrast on the plum ground.
  • Added the vignette and edge-glow in the display shader; flat-mapped spins read as a bitmap, the vignette gives the field depth.
Pass 2 · Depth
  • Enriched the signature: the magnetization plot now overlays both Onsager branches (±M) so the live dot visibly chooses a branch as the lattice orders, and a fading trail records the path through Tc.
  • Added the phase pill (Ordered / Critical / Disordered) that recolours to the winning spin — the second-read detail is that its hue matches whichever domain is dominating.
  • Tuned the sweep speed and reversal points so an auto-sweep glides through Tc slowly enough to watch domains melt, then reforms them on the way back down.
  • Warmed the lattice with 160 pre-roll steps before first paint so the resting state opens already mid-convergence — large domains shimmering, never a random-static cold start.
Pass 3 · Hardening
  • 375px: the console collapses to one column, the secondary nav link hides below 400px so the wordmark and index link never collide, and the slider stays full-width and thumb-reachable.
  • Reduced-motion: the loop never starts; instead the lattice runs 400 settle steps to a clear domain pattern, paints one static frame, and plots one dot — a premium still, not a frozen blank.
  • Focus-visible rings on the slider thumb, both buttons, and every link; the loop pauses on document.hidden and resumes on return; DPR is capped at 2 and the canvas resizes to its container.
  • Chanel rule — removed the plot's outer halo ring around the live dot; the filled dot with its dark keyline reads cleaner against the Onsager curves. Also seeded a gentle 53% symmetry-break so the resting lattice orders onto one branch and the magnetization dot rides the curve instead of floating near zero. Tightened the final copy read.