Web applications have historically been strangely silent.
Desktop operating systems and classic terminal environments have long used tiny auditory cues to acknowledge actions. A key press, a notification, a system error, or even a simple toggle can produce a barely noticeable sound that tells you, without looking, that something happened.
The web rarely does this well.
But that doesn't mean it has to stay that way.
With the browser's native Web Audio API, interfaces can generate small, responsive sounds directly in the browser. No MP3 library, no collection of audio files, and no extra assets to manage.
I recently added this idea to my own site using Cuelume, a small library for synthesized interaction sounds.
Sound is an interesting form of feedback because it doesn't compete for the same attention as the visual interface.
A button can change color.
A modal can animate.
A notification can appear.
But a tiny sound can confirm that something happened without requiring you to look at it.
This is particularly useful for interactions such as:
The important part is that these sounds should remain subtle.
The goal isn't to turn a website into a video game. It is to make interactions feel slightly more physical.
The traditional approach would be to ship a collection of .mp3, .wav, or .ogg files and play them when an interaction happens.
For a handful of tiny UI sounds, that can feel unnecessarily heavyweight.
Synthesizing sounds in the browser gives you:
This is where the Web Audio API becomes interesting.
Instead of saying:
"Play this file."
you can essentially say:
"Generate this sound."
An oscillator can produce a tone, a gain node can shape its volume, and frequency changes can turn a simple tone into something that feels like a click, chirp, bloom, or sweep.
Rather than building an entire sound system from scratch, I used Cuelume.
Cuelume provides a curated collection of interaction sounds that are synthesized live using the Web Audio API. It ships without audio files and has zero runtime dependencies.
The library includes sounds for things like:
tickpressreleasetogglesuccesserrorbloomchimesparklepulsearrivalThat makes it much easier to experiment with sound without having to become an audio designer first.
The basic setup is intentionally simple:
typescriptimport { bind } from "cuelume";bind();
You can then attach sounds directly to elements:
html<button data-cuelume-press data-cuelume-release>Save</button><a data-cuelume-hover="tick">Docs</a><button data-cuelume-toggle>Dark mode</button>
Or trigger a sound programmatically:
typescriptimport { play } from "cuelume";await navigator.clipboard.writeText(text);play("success");
This is one of the things I like about the library. The component doesn't need to know anything about oscillators, frequencies, gain envelopes, or audio contexts.
It just says:
this interaction should sound like this.
A useful way to think about interface audio is to treat it like animation.
You probably wouldn't animate every single element on a page.
Instead, motion is useful when it communicates something:
Sound follows the same principle.
A few carefully chosen sounds are much better than attaching audio to every click.
For example:
| Interaction | Sound |
|---|---|
| Button press | press |
| Button release | release |
| Toggle | toggle |
| Successful action | success |
| Recoverable error | error |
| Navigation | arrival |
| Hover | tick |
| Reveal | bloom |
The exact mapping isn't important.
The consistency is.
Once a user subconsciously learns that a certain sound means "something succeeded", the sound becomes another piece of the interface language.
The best UI sounds are often the ones you barely notice.
For small interactions, I prefer sounds that are extremely short and quiet.
A few useful rules:
If you consciously notice the sound every single time you click something, it is probably too loud or too distinctive.
The ideal reaction is closer to:
"Oh, that felt responsive."
rather than:
"Why is this website making noises?"
Another problem with UI audio is repetition.
If the exact same sound plays hundreds of times, your brain starts noticing it.
Small variations can make repeated interactions feel less robotic.
For example, instead of always generating exactly the same pitch, a sound system can vary the frequency slightly:
typescriptconst frequencies = [420, 440, 460, 480];const frequency =frequencies[Math.floor(Math.random() * frequencies.length)];
The variation should be small.
You want the sounds to feel like the same interaction, not four completely different effects.
Libraries such as Cuelume make this experimentation easier because you can focus on choosing the right sonic character instead of implementing the underlying synthesis yourself.
Audio should always be an enhancement.
The website should remain completely usable with sound disabled.
Cuelume provides controls for enabling/disabling playback and adjusting the global volume:
typescriptimport { setEnabled, setVolume } from "cuelume";setVolume(0.4);setEnabled(false);
The application should own the actual user preference, so a site can expose something like:
Settings → Sound effects → On / Off
and persist that preference however it already handles user settings. Cuelume itself does not persist the preference.
It's also worth respecting accessibility preferences for motion and keeping audio non-essential. prefers-reduced-motion is not technically an audio preference, so it shouldn't automatically mean "mute everything", but it can be useful when designing a broader reduced-effects mode.
Browsers generally won't allow arbitrary audio playback immediately when someone opens a page.
The user needs to interact with the page first.
This is one reason interaction-triggered sounds work particularly well. A click, key press, toggle, or other intentional action provides the browser with the user interaction needed to start audio playback.
Cuelume also handles a number of these browser concerns internally, including a shared lazy AudioContext, suspended contexts, SSR-safe imports, and graceful fallback when Web Audio isn't available.
That means the rest of the application can mostly think in terms of sound semantics, rather than browser audio plumbing.
What I find interesting about this isn't really the sound itself.
It's the idea that a web interface doesn't have to communicate exclusively through pixels.
We already use:
Adding a tiny amount of audio can make an interface feel considerably more responsive without making it feel noisy.
And because modern browsers already give us the Web Audio API, this doesn't necessarily require shipping a pile of audio assets.
A few lines of code can create something that makes a button feel physical.
That's a surprisingly powerful little detail.
If you want to experiment with this, check out Cuelume. It is open source under the MIT license and available on npm.
Start with one or two interactions.
Keep the volume low.
And if you can use your website for ten minutes without thinking about the sounds, you've probably done it right.
Sound should make an interface feel more responsive, not make the interface louder.
Crafting software, ricing Linux & building local-first tools.