UI Sound Kit
Lightweight interface sounds — click, hover, success, error and more — synthesized live with Web Audio, with built-in volume control, a global switch, and preference-respecting behavior; no audio files, no extra requests.
npx shadcn@latest add https://webberui.com/r/ui-sound-kit.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<UiSoundKit />
Installation
npx shadcn@latest add https://webberui.com/r/ui-sound-kit.jsonOr, once registries are configured in components.json, install it as @webberui/ui-sound-kit.
Usage
Wrap SoundProvider once around the scope that needs sound, and any component inside it can get play() plus the switch/volume controls through useSound():
import {
SoundProvider,
useSound,
} from "@/components/ui/ui-sound-kit";
function App() {
return (
<SoundProvider defaultVolume={0.5}>
<SaveButton />
</SoundProvider>
);
}
function SaveButton() {
const { play } = useSound();
return (
<button
onClick={async () => {
try {
await save();
play("success");
} catch {
play("error");
}
}}
>
Save
</button>
);
}Built-in sound names: "click", "hover", "success", "error", "toggle", "notification" (the full list is available from SOUND_NAMES).
Built-in components
SoundButton is a button that makes sound, playing automatically on click (and optionally on hover); SoundToggle is the global switch button:
import {
SoundButton,
SoundToggle,
} from "@/components/ui/ui-sound-kit";
<SoundButton sound="click" hoverSound="hover">
Play
</SoundButton>
<SoundToggle />Props
SoundProvider
| Prop | Type | Default | Description |
|---|---|---|---|
defaultEnabled | boolean | true | Whether sound starts enabled (overridden after mount when a persisted preference exists) |
defaultVolume | number | 0.5 | Initial global volume, 0 to 1 |
storageKey | string | null | "wb-sound-kit" | localStorage key that persists the switch and volume; null disables persistence |
respectReducedMotion | boolean | true | Whether to also mute when the system has "reduce motion" enabled |
What useSound() returns
| Name | Type | Description |
|---|---|---|
play | (name, options?) => void | Synthesizes and plays a built-in sound; options.volume is a one-off volume multiplier |
enabled | boolean | Whether global sound is on |
setEnabled | (enabled: boolean) => void | Turn global sound on/off |
volume | number | Global volume, 0 to 1 |
setVolume | (volume: number) => void | Set the global volume (automatically clamped to 0–1) |
SoundButton
| Prop | Type | Default | Description |
|---|---|---|---|
sound | SoundName | "click" | The sound played on click |
hoverSound | SoundName | — | When set, this sound also plays as the cursor moves in |
All other native button attributes are forwarded. SoundToggle forwards native button attributes too, and uses chime (default true) to control whether a confirmation sound plays when it is switched on.
How it works
- Synthesized live, zero assets: sounds are synthesized on the fly with Web Audio's
OscillatorNode+GainNode— no audio file is loaded and no extra request is made. - Lazy initialization: the
AudioContextis created and auto-resumed only on the firstplay()(usually from a user click gesture), which complies with browser autoplay policies; it also tries to recover if it wassuspended while the tab was in the background. - A single audio graph: the whole Provider shares one
AudioContextand one master gain node; every playback attaches its own bus gain, applies the one-off volume multiplier, and is then recycled. - Persisted preferences: the switch and volume are written to
localStorageby default and carried across refreshes; turn this off withstorageKey={null}.
Accessibility
SoundToggleexpresses its state witharia-pressed, and switches itsaria-label(enable sound/disable sound) and icon along with the state.- The volume slider is a native
<input type="range">with anaria-label, operable by keyboard. - When the user has "reduce motion" enabled at the system level,
respectReducedMotionmutes by default, respecting the preference for less stimulation; with sound off, interaction behavior (onClickand so on) is unaffected — it simply makes no sound.
Broadcast Bar System
A top broadcast bar system that stacks multiple announcements, rotates them through a priority queue, supports countdowns, CTAs, and dismissal memory, and springs its height changes so the content below is pushed smoothly.
Live Glance Strip
A live-activity information strip — the iOS Live Activity vocabulary brought to the web as a frosted-glass pill, with status text flipping vertically on update, a progress track advancing live, and details expanding on click.