Skip to content
On this page 1. Off by default

Getting started

Sound principles

The rules the sound entry enforces in code, and why: off by default, no sound before a gesture, subtle, never the only signal.

Interface sound is decoration over accessible feedback. It confirms something that already happened, and it can be removed without losing information. These rules are not advice: the engine in @codeatlasdev/ui/sound enforces each one, and a test fails when it stops.

1. Off by default

Sound runs only after the user turns it on with a visible control (SoundToggle). The choice is persisted in localStorage under ca-sound as { enabled, volume }, so it survives a reload and nobody has to turn it on twice. A page that surprises people with noise teaches them to mute the tab, and then your real alerts go unheard.

2. Nothing before a user gesture

Browsers block audio until the user acts, and an AudioContext created earlier is suspended and logs a warning. The engine creates its single shared context only inside the first pointer or key event after sound is on. Until then no AudioContext exists at all, which a unit test asserts.

3. Subtle and short

The master volume defaults to 0.25. Every cue is short (most are under 150 ms), nothing loops, and a compressor on the master bus keeps stacked cues from clipping. A cue the user stops noticing is working; one they notice every time is too loud.

4. Never the only signal

Every cue duplicates something visible or announced: the pressed state, the toast text, the live region, the focus ring. Never use sound to carry the only copy of an error, a result or a status. Someone who is deaf, on mute, in a meeting or using a screen reader must lose nothing. SoundToggle itself exposes its state with aria-pressed and has an accessible name.

5. Respect reduced motion and context

Under prefers-reduced-motion: reduce, hover and slider tick cues are off; feedback cues still play if the user enabled sound. Sound is suspended while the tab is hidden, cut off when the page unloads, and skipped while a <video> or <audio> element is playing.

6. Throttle and dedupe

One cue per name per 40 ms, and at most four voices at once (the oldest is stolen). A fast typist or a dragged slider never turns into a buzz.

7. The sound matches the style

A cue is part of the look. The profile follows the design style: brutalism is dry and hard, glass is soft and bright, terminal is square-wave beeps. Do not mix a chime into a brutalist screen; pick the profile that fits, or tune --ca-sound-gain, --ca-sound-pitch and --ca-sound-brightness.

See Sound for the API and a live demo of every profile.