Skip to main content
Back to writing
2 min read

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.

web-audiotypescriptdesign

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-motion set, 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.