Seven interface sounds — hover, click, open, close, success, error, type — mapped to the events you already fire. Every one is built at runtime from oscillators, a generated noise buffer, filters and envelopes. There are no audio files to host, no licences to clear, and the whole layer weighs a few kilobytes.
Sound is off until you ask for it. The toggle in the corner is the switch, the choice is remembered, and no audio context exists until you touch the page — browsers block it otherwise, and so does this. Press H to hide the control panel.
Each row is one recipe: a stack of voices, each with its own wave or noise burst, envelope, filter sweep and start offset. Change the preset in the panel to hear the same seven events rebuilt with a different palette.
| Event | Voices | Level |
|---|
audio context none · voices ringing 0 · sound off
Nothing below calls play(). The layer listens once at the
document and reads the markup: buttons and links get hover and click, anything with
data-sound="open" gets the drawer sound, and typing in a field ticks.
Opening plays open, closing plays close — the button
swaps its own data-sound value, so the map never needs to know
about state.
Three rules, and the engine keeps all of them for you.
| Off by default | enabledByDefault is false. A first-time visitor hears nothing. |
| Remembered | The choice is written to localStorage under your own key, and blocked storage is not an error. |
| Gesture first | Even with sound remembered as on, no AudioContext is created until a click, tap or keypress. |
| Always switchable | The toggle is a real button with aria-pressed, fixed on screen, reachable by keyboard at any point. |
| Quiet in the background | Hiding the tab suspends the context; coming back resumes it. |