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.
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.
The dusk gradient behind the tower — the hour after the last air, night arriving from the top:
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.
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.
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.
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.
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.