← Back to the museum

How this site was made

The Whirligig is a fictional children's science museum about motion and forces. The site's job is to plan a visit and radiate play — so the design itself had to obey physics, not just describe it.

Concept & art direction

The hero is a real 2D physics simulation: the letters of WHIRLIGIG tumble in with balls and gears, collide, roll, and can be grabbed and flicked. Exhibits are printed like admission tickets, each carrying a tiny working physics machine instead of a static icon. Visit info is one big friendly poster — the thing you'd actually pin to a fridge.

The palette is bright but grown-up: cobalt and ink do the structure, sunshine / tomato / mint do the play. Nothing pastel, nothing babyish — kids get color, parents get contrast and legibility.

Palette

Paper#FAF5EA
Ink#1B2050
Cobalt#2443CE
Sunshine#FFC526
Tomato#F1502F
Mint#5BC79B

Every colored surface gets a 3px ink border and a hard offset shadow — the whole site reads like die-cut cardboard toys on a table.

Type pairing

Baloo 2

Chunky, rounded, almost inflatable — letters that look like physical objects, which is literally what they become in the hero canvas.

Weights 500–800 · headlines, tickets, buttons

Atkinson Hyperlegible

Designed by the Braille Institute for maximum character distinction. The parent-facing voice: warm, plain, unmistakably readable at small sizes.

Regular + bold · body copy, visit details

The signature element: hand-rolled verlet physics

No physics library. Each body stores its position and its previous position; velocity is implied by the difference. That makes flicking trivial — on release you just set prev behind the pointer's velocity:

function integrate(b) {
  var vx = (b.x - b.px) * AIR;   // velocity = position history
  var vy = (b.y - b.py) * AIR;
  b.px = b.x; b.py = b.y;
  b.x += vx;
  b.y += vy + GRAVITY;
}

// on pointerup — the flick:
grabbed.px = grabbed.x - pointer.vx * 0.9;
grabbed.py = grabbed.y - pointer.vy * 0.9;

Collisions are solved positionally, three iterations per frame: overlapping circles push apart in proportion to mass (r²), and the tangential rub between them is converted into spin so letters visibly roll off each other:

var relT = (avx - bvx) * -ny + (avy - bvy) * nx; // rubbing speed
a.angVel += relT / a.r * 0.06;
b.angVel -= relT / b.r * -0.06;

The six ticket icons are separate honest simulations sharing one rAF loop: the cradle hands energy between end balls, the gears rotate at a locked 10:7 tooth ratio, and the spinner speeds up as its dots pull inward because L = I·ω is held constant. With prefers-reduced-motion, the hero pre-settles 700 steps off-screen and draws a single calm frame.

The three iteration passes

  1. Correctness & composition. The drag hint was buried behind the letter pile — moved it into a dashed pill at the top right. On mobile the buttons collided with the letters, so hero copy moved to the top and the pile owns the floor. A dotted throw-trajectory arc now fills the hero's dead air. Zero console errors confirmed.
  2. Elevate. Hard landings now kick up little dust puffs in the hero sim, and hovering (or tapping) any exhibit ticket kicks its machine — the pendulum pumps, the gears get cranked, the wind tunnel gusts, the drop ball gets tossed. Hero type scale got a bump.
  3. Taste. Chanel rule: deleted the mint background blob that crowded the pile. Fixed an awkward headline wrap, slowed the marquee from 22s to 30s, and lifted the hero copy clear of the tallest letters.

Do this yourself

  1. Pick a subject with a physical truth you can simulate — motion museum → physics; bakery → rising dough; observatory → orbits. Let the code demonstrate the subject.
  2. Ask Claude for a verlet integrator before reaching for Matter.js — position + previous position is 20 lines and makes grab-and-flick natural.
  3. Choose one display face that embodies the subject physically (rounded = bouncy) and one body face chosen for the reader, not the brand.
  4. Lock a small palette and one construction rule (here: ink borders + hard shadows) so every component looks die-cut from the same sheet.
  5. Make structure carry information: our ticket headers encode room number and the actual force each room teaches.
  6. Replace static icons with tiny true simulations — a 90px canvas with 30 lines of math beats any illustration pack.
  7. Screenshot, critique, fix — three times. Read the screenshots like a stranger: spacing rhythm, contrast, what fights the hero.
  8. Ship the calm version too: reduced-motion users should get a settled, beautiful still — never an empty box.