How The Hour After was made

Build notes for the Sint-Maren Carillon — a fictional civic instrument in the invented river town of Alderbruk. The page has one job: put a real carillon under your hands, mass and all, and make the lag of the big bells feel like a fact about bronze rather than a bug.

Concept

A carillon is the least forgiving keyboard instrument there is: no amplification, no quantisation, and a clapper on the bourdon that weighs more than the player. The site is a cutaway belfry of seventeen procedural SVG bells above a baton console. You freestyle from the keyboard (A–; naturals, W–P sharps) or run one of four traditional airs from punched oak-tag cards. Everything — sound, swing, lag — is computed, nothing is sampled.

Palette

The dusk gradient behind the tower — the hour after the last air, night arriving from the top:

Bell bronze
#8C6A3F
The body colour of the instrument; every warm tone derives from it.
Polished bronze
#C9A46A
Highlights, borders, the live state of a playing card.
Tower stone
#3B3833
The masonry that frames the bells; the page itself is built of it.
Verdigris
#6FA08B
Weathering on the 1911 castings, links, focus rings — age as an accent.
Oak-tag paper
#EDE6D6
Body text, and the punched cards the airs are cut into.

Type

The Hour After

Fraunces — a wonky old-style with real optical sizing. Its italics name the bells (Great Maren, Petronella) the way a founder's ledger would; the soft ink-trap details read as cast metal, not print.

Baton console, C4 – E5

Jost — a geometric sans of the same early-20th-century moment as the 1911 casting date. It does the labour: labels, hints, tables, captions, all at light weights.

Techniques

Additive bell synthesis (WebAudio). Each strike builds a bell from eight sine partials in the classic minor-third profile — hum at half the strike note, prime, tierce at 1.1892, quint, nominal, deciem — each with its own amplitude and decay share. Bells over 180 kg get a detuned twin on the hum (+0.7 Hz) so the low bells beat slowly, the way real bronze does. A bandpass-filtered noise snap supplies the clapper transient, and everything passes through a convolver whose impulse response is 2.6 s of procedurally generated decaying noise — the tower chamber, built from Math.random().

The signature: clapper lag. Every bell's latency is computed from its mass — lag = 60 ms + 275 ms · (w/612)0.85 — so Petronella (38 kg) answers in 86 ms and Great Maren (612 kg) in a third of a second. The audio event is scheduled at AC.currentTime + lag; the SVG clapper animates its pull across the same interval, and the visible strike, flash, and bell rock all land with the sound. The punched-roll player compensates like a carillonneur: it schedules the pull early by each bell's lag so the airs stay in time — watch the read bar and the big holes fire ahead of the beat.

Swing inertia. Bell rock is a damped spring whose stiffness scales inversely with bell radius, so small bells flutter and the bourdon sways slowly. Strikes alternate sides, as a real clapper does.

Procedural everything else. The belfry (stone arch, beams, clock, pigeons, seventeen bells sized by w1/3) is generated SVG; the tower clock reads the visitor's actual time; the punched cards are drawn from the same note data that drives the audio; the dusk stars are seeded-random canvas 2D, DPR-capped at 2. No libraries, no images — one HTML file.

Iteration log

  1. Pass 1 — Craft

    Re-measured every text/background pair. The console hint (dark text on the ember band of the dusk gradient) computed at 4.0:1 — under the floor — so it darkened from #40301f to #2e2315 (≈4.9:1). The air cards' play buttons changed glyph but not label when live; they now read “Silence the tower”. Removed a leftover no-op (Math.max(720, 720)) in the roll viewBox from the interrupted belfry rework.

  2. Pass 2 — Depth

    Wired up the belfry caption that the first build left orphaned: an aria-live line under the bells now names each bell as its clapper lands — Great Maren — C4 · 612 kg — so the lag is legible in words as well as sound. Because it fires at strike time, not keypress time, the caption itself arrives late on the big bells. Existing second-read details kept: the pigeons on the lower beam that scatter when the bourdon speaks, and the tower clock set to your real time.

  3. Pass 3 — Hardening

    Found that the baton buttons only listened to pointerdown, so keyboard users tabbing to a baton couldn't strike it — added keyboard-click handling. Added a <noscript> style plus an independent fallback script so the stage can never sit at opacity 0 if the main script fails. The physics loop now bails immediately when the tab hides. Re-checked 375 px (key labels hide, console compresses), the reduced-motion path (no swing, but the strike flash and the honest audio lag remain), and focus rings on bells, batons, chips, and table rows.