RESONANCE is one of 25 sites designed and built by Formwork to demonstrate web craft, motion, and taste. This one is about listening — the hero is a colour field that breathes to sound the browser makes itself. Here is exactly how it works.
The brief was "deep, warm, sonic." Instead of a video loop or a static gradient, the hero is a living mesh-gradient — five soft colour blobs drifting through a teal-black field — that pulses to audio. Because browsers rightly block autoplay, the page can't assume sound. So it runs in two modes: silent-but-alive by default, and reactive once you press play. Same visual code path, two spectrum sources.
AnalyserNode.main branch.Press play and the site builds an ambient pad from scratch — a detuned Cmaj7
chord (C, E, G, B over a low C) where every note is two slightly-detuned oscillators, plus slow
LFOs on the master gain and filter so the chord breathes and evolves. It routes through an
AnalyserNode, and every frame we read the spectrum and let it move the blobs:
// one shared buffer — the analyser OR a synthetic sine fills it
if (!audio.sample()) { synthSpectrum(t); synthWave(t); }
// each blob grows and brightens with its own frequency band
const band = bandEnergy(b.band[0], b.band[1]);
const r = min * (b.base + band * b.react + energy * 0.08);
const g = ctx.createRadialGradient(px, py, 0, px, py, r);
g.addColorStop(0, `rgba(${c},${a})`);
ctx.globalCompositeOperation = 'lighter'; // blobs add into a glow
Before any click, synthSpectrum() fabricates a gentle, moving spectrum from a few
sine LFOs, so the hero is never dead — it just happens to be silent. Muting fades the pad out and
drops straight back to that synthetic source. The two modes are indistinguishable to the render loop.
AudioContext is created inside the click handler and resumed there — the only place browsers allow it.AudioContext? The button disables itself and the visuals keep animating synthetically. No console errors either way.prefers-reduced-motion the loop never starts: one static mesh frame renders and all reveals are forced visible.The whole site is static, so hosting is three commands:
git init -b main && git add -A && git commit -m "ship"
gh repo create formwork-resonance --public --source=. --push
gh api --method POST /repos/OWNER/formwork-resonance/pages \
-f 'source[branch]=main' -f 'source[path]=/'
Use relative paths (./main.js, not /main.js) because
project Pages live under /repo-name/. Add an empty .nojekyll file so
every asset serves verbatim.
That's the recipe: two canvases, a chord the browser plays to itself, and a synthetic understudy so the page is alive even in silence. The other 24 sites each swap the central technique — shaders, particles, kinetic type — but the discipline is the same.