Synthesizing UI Sounds with the Web Audio API
Zero audio files, a few oscillators, and a sound design budget of about forty lines of code.
This site makes little sounds when you hover the nav or click a card — if you opt in. None of them are audio files. They're synthesized in the browser with the Web Audio API, which means they add nothing to the bundle.
A sound is just an envelope on an oscillator
Each effect is a tiny voice: a waveform, a frequency, and a gain envelope short enough that it reads as a tick rather than a tone.
const osc = ctx.createOscillator();
const amp = ctx.createGain();
osc.type = "triangle";
osc.frequency.setValueAtTime(440, start);
osc.frequency.exponentialRampToValueAtTime(330, end); // satisfying pitch drop
amp.gain.setValueAtTime(0.0001, start);
amp.gain.exponentialRampToValueAtTime(0.08, start + 0.01); // quick attack
amp.gain.exponentialRampToValueAtTime(0.0001, end); // smooth decay
The exponential ramps matter. Without them you get a click at the start and end of every note as the gain snaps between values.
Defaults are a design decision
Three rules keep it from being annoying:
- Off by default. Sound is opt-in via a toggle in the nav.
- Remembered. The preference lives in
localStorage. - Respectful. If the visitor has
prefers-reduced-motionset, audio is disabled outright and the toggle disappears.
A differentiating detail should never become a tax on the people who didn't ask for it. Synthesis keeps it weightless; the defaults keep it polite.