@yugo.click/sound
v0.1.0
Published
Fourteen curated interaction sounds, synthesized live with the Web Audio API. No audio files, no dependencies.
Maintainers
Readme
@yugo.click/sound
Fourteen interaction sounds for the web, synthesized live with the Web Audio API. No audio files, zero runtime dependencies.
This is the sound half of yugo. The rest of yugo is copy-paste component files; sound is the one piece that is a package, because a synthesis engine is not something you want four copies of.
Install
bun add @yugo.click/soundESM only. Server-side imports are safe; playback only happens in the browser.
Quick start
Declaratively, with attributes:
<button data-yugo-press data-yugo-release>Save</button>
<a data-yugo-hover="tick">Docs</a>
<button data-yugo-toggle>Dark mode</button>import { bind } from "@yugo.click/sound";
bind();| Attribute | Fires on | Default sound |
| ------------------- | -------------- | ------------- |
| data-yugo-hover | pointerenter | chime |
| data-yugo-press | pointerdown | press |
| data-yugo-release | pointerup | release |
| data-yugo-toggle | click | toggle |
Leave the value empty for the default, or set it to any sound name.
Or imperatively:
import { play } from "@yugo.click/sound";
await navigator.clipboard.writeText(text);
play("success");
play("success", { volume: 0.4 });Preferences live in your app; this package only applies them:
import { setEnabled, setVolume } from "@yugo.click/sound";
setVolume(0.7);
setEnabled(false);Nothing is read from or written to storage.
Sounds
| Name | Character | Suggested use |
| --------- | --------------------------------- | -------------------------------- |
| chime | Soft two-note ascending bell | Default hover |
| sparkle | Quick four-note twinkle | Playful accents |
| droplet | Single note gliding down | Dismiss, collapse |
| bloom | Warm slow swell | Reveal, expand |
| whisper | Breathy quiet swell | Dense lists |
| tick | Crisp instant tick | Nav and menu hover |
| press | Dull muted knock | Pointer down |
| release | Brighter springy tick | Pointer up |
| toggle | Mechanical click-clack | Switches, tabs |
| success | Warm three-note confirmation | After an action succeeds |
| error | Soft knock and descending refusal | Recoverable errors |
| page | Papery flick with a glass tick | Pages, galleries, carousels |
| loading | Brief unresolved rising shimmer | User-initiated work starting |
| ready | Focus tick with a harmonic bloom | Image or content loaded |
API
import {
play,
bind,
setEnabled,
setVolume,
isEnabled,
getVolume,
getAnalyser,
subscribe,
sounds,
type SoundName,
} from "@yugo.click/sound";play(name?, options?)— play immediately. Defaults to"chime";options.volumescales this play only.bind(root?)— delegate everydata-yugo-*interaction underroot(default: the document). Idempotent, and later DOM additions work without rescanning.setEnabled(enabled)/isEnabled()— gate future playback.setVolume(volume)/getVolume()— global multiplier, clamped to0–1.getAnalyser()— theAnalyserNodeon the shared output, for drawing what is being heard. Brings the context up, so it is safe before the first play.subscribe(listener)— called with the name of every sound that reaches the speakers, including ones fired bybind(). Returns an unsubscribe.sounds— every sound name.SoundName— the union type.
Defaults that behave
- Pointer-aware. Hover needs a fine mouse. Press and release cover mouse, touch, and pen; toggle follows native click activation, keyboard included.
- Hover repeat guard. Hover is globally throttled to one every 150ms, so sweeping a menu stays quiet.
- Audible without clipping. One boosted output stage, with native compression protecting overlapping cues.
- One lazy
AudioContext, shared across every sound. - Autoplay-friendly. Silent until the page has had a user gesture, and resumes a suspended context without surfacing errors.
- SSR-safe. Importing on the server is a no-op.
- Safe fallback. Unknown names and blocked or unavailable Web Audio make
play()a silent no-op.
React
The React sheets built on this package — a cue palette, a sound toggle, a cue button, and a waveform scope — live at yugo.click, under Sound. Each is one file you copy in.
useEffect(() => {
bind();
}, []);License
MIT. See LICENSE.
