Task 6 of 6

Drag the Temperature Across Tc

Everything is wired. The payoff is one number you can move with your finger.

Start from the coldest configuration there is — every spin up — and run 150 coloured sweeps at a temperature you choose. Below the critical temperature the lattice keeps its order: thermal noise chews holes in it, but the holes heal and the magnetisation m, the average spin, sits stubbornly near ±1. Above it the order does not survive at all — the lattice dissolves into salt and pepper and m falls to zero. In between there is no gentle slope; the whole thing turns over inside a few tenths of a degree. Onsager solved this exactly in 1944 and the answer is Tc = 2 / ln(1 + √2) ≈ 2.269, in units of J / k_B.

Two honest caveats, because a lattice of 16,384 spins is not infinite and 150 sweeps is not forever. The crossover you can see sits a little above 2.269 — finite lattices round a transition off, and a cold start clings to its order — and right at Tc the model slows to a crawl, which is not a bug in the simulation but the defining symptom of a critical point. Drag slowly through 2.32.5 and watch the domains grow to the size of the whole box just before they let go.

the exact answer drops off a cliff at 2.269; 16,384 spins in 150 sweeps take the corner wide — Magnetisation against temperature, with the exact Onsager curve dropping vertically to zero at 2.269 and the measured 128 by 128 curve hanging on until about 2.4 before collapsing.
Goal: declare the temperature slider, paint the lattice, and render a frame every ten sweeps so the run becomes something you can scrub through.

Requirements

Hint 1 — the slider is the program

slider() returns the value this run is using and puts the control under the console; moving it re-runs the whole program from the top. That is the entire model — your code is a pure function of its controls, so there is no event loop to write.

const temperature = slider('temperature',
  { min: 1.5, max: 3.5, value: 2.25, step: 0.05 });
Hint 2 — three renders make a scrubber

Consecutive render() calls collapse into a frame strip with a slider under it, so rendering inside the loop costs you nothing and gives you the whole history:

if (k % 10 === 0) {
  await paint(s);
  render(paint.canvas);
}

Keep the two lines adjacent and do not console.log between them — a log line in the middle breaks the run of frames into separate images.

Same idea elsewhere

A control that re-runs the whole computation is the interaction model of every GPU toy worth playing with, and it works for the same reason here as in a shader: the frame is cheap enough that recomputing it beats maintaining incremental state. The physics travels further than the code. Order parameters, critical exponents and finite-size scaling are the vocabulary of everything from magnets to percolation thresholds to the training dynamics of large models, and the Ising lattice is where all of it was worked out first.

All tasks in The Ising Model: Colour to Break the Race

  1. What a Flip Would Cost
  2. Randomness Without a Random Number Generator
  3. Everyone at Once Is Wrong
  4. Colour the Lattice
  5. Two Halves Make a Sweep
  6. Drag the Temperature Across Tc

This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.