Search documentation
Search pages, components, blocks, styles and colours.
Short, quiet interface sounds synthesized in the browser, on by default.
Installation
This adds lib/sounds.ts, the sound engine, and components/sound-effects.tsx, which plays sounds for every component. There are no audio files and no dependencies: each sound is a few short layers of oscillators and filtered noise, made with the Web Audio API when it plays.
The catalogue
Thirty-two sounds, each made for one kind of component. Every family has its own material, so you can tell what you touched without looking:
- Buttons are wood: a knock that gets heavier from secondary to primary, and a damped thud for destructive actions.
- Selection controls are a mechanism: a checkbox clicks twice like a check mark being drawn, a toggle presses in and catches, and a radio clicks and rings.
- Switches are liquid: a soft, round drop as the thumb settles on, and a quieter sigh as it lets go.
- Rows and links are glass: a tick light enough for every menu item.
- Tabs are bells, tuned to a chord across their positions.
- Surfaces each move differently: menus pop, dialogs bloom on a fifth, alert dialogs open on a minor third, sheets and drawers move air, and accordions unfold.
- Toasts chime by type: info, success, warning and error each have their own.
Press one to hear it.
Actions
Selection
Navigation
Surfaces
Notifications
Movement
Feedback
Set up
Render <SoundEffects /> once, near the root of your app. Sounds play as soon as it mounts. Pass the user's preference to let them turn sounds off:
"use client"
import { SoundEffects } from "@/components/sound-effects"
export function Providers({
soundsOn,
children,
}: {
soundsOn: boolean // the user's setting, however you store it
children: React.ReactNode
}) {
return (
<>
<SoundEffects enabled={soundsOn} />
{children}
</>
)
}Sounds are on by default. Set enabled to false, or call setSoundsEnabled(false), to silence them. Nothing in the components changes: <SoundEffects /> listens on the document and picks a sound from the control's role and data-slot, which are the same in every base.
<SoundEffects />.What plays when
Small buttons sound slightly higher and large ones slightly lower. Base UI's Button doesn't set data-variant, so the variant is read from the classes every style gives it.
Triggers that open a popup stay quiet, so opening a menu plays one pop rather than a tap and a pop. A click that closes a popup plays only one sound: picking an option plays pick, and Cancel plays the surface closing. Surfaces only sound when someone has just clicked or typed, so a hover card or navigation menu opening under the pointer stays quiet. A loading toast plays when it settles into success or error. Tooltips and the sidebar toggle never play: they happen too often.
Choose a sound
Set data-sound on any element to choose its sound, or data-sound="none" to silence it and everything inside it:
<Button data-sound="copy" onClick={copyLink}>
Copy link
</Button>
<section data-sound="none">{/* a quiet area */}</section>Play sounds yourself
import { playSound, useSound, useSoundsEnabled } from "@/lib/sounds"
// Anywhere: plays only when sounds are on.
playSound("success")
// In a component: a stable callback.
const playNotify = useSound("notify")
// A settings toggle.
const [enabled, setEnabled] = useSoundsEnabled()The same sound can't repeat within 35ms, and no more than six play at once, so a fast drag or a burst of events stays quiet. Each play is detuned by about 1%, so a sound repeated many times doesn't sound mechanical.
Accessibility
- Sounds start on. Give people a visible switch to turn them off, and remember their choice.
- Sound is never the only feedback: every interaction that plays one also changes on screen.
- Nothing plays while the tab is hidden, and the audio engine only starts after someone interacts with the page, as browsers require.
Credits
The catalogue's roles are inspired by Bencho's sound library. The sounds themselves are original, synthesized by lib/sounds.ts.