Documentation

Everything cuelume exports, and when to reach for it.

Six exports and four attributes. Most projects only ever need bind(). The rest is here for when you want a sound at an exact moment, or want the listener to be able to turn it down.

Install

Install Cuelume, then import it wherever your interface runs.

$

Click the command to switch package manager. ESM-only, zero runtime dependencies, and safe to import during SSR.

Quick start

Mark up the elements that should make a sound, then wire them all at once. Nothing else is needed.

index.html
<button data-cuelume-press data-cuelume-release>Save</button>
<a data-cuelume-hover="tick">Docs</a>
<button data-cuelume-toggle>Dark mode</button>
<button data-cuelume-press="pulse" data-cuelume-release="scan">Launch</button>
app.ts
import { bind } from "cuelume";

bind();

Attributes

Each attribute covers one behavior. Leave the value empty to use its default sound, or set it to any name from the catalog.

Attribute Fires on Default
data-cuelume-hover Fires onpointerenter Defaultchime
data-cuelume-press Fires onpointerdown Defaultpress
data-cuelume-release Fires onpointerup Defaultrelease
data-cuelume-toggle Fires onclick Defaulttoggle

Pair data-cuelume-press with data-cuelume-release to turn a click into a real two part press.

bind()

bind(root?: ParentNode): void

Delegates every data-cuelume-* interaction under root, which defaults to the whole document. Call it once at startup.

It is idempotent, so calling it again is harmless, and it handles elements added later, so you do not need to re-bind after a render.

app.ts
import { bind } from "cuelume";

bind();                               // the whole document
bind(document.querySelector("#app")); // or one subtree

play()

play(name?: SoundName, options?: { volume?: number }): void

Plays a sound immediately, for moments that are not a DOM interaction: a request that resolved, a copy that landed, a form that failed. Defaults to chime.

options.volume scales this one play only and leaves the global level alone.

app.ts
import { play } from "cuelume";

await navigator.clipboard.writeText(text);
play("success");
play("ready");                           // content is ready
play("arrival");                         // client-side page arrival
play("success", { volume: 0.4 });     // quieter, just this once

Browsers block audio on a fresh visit until the listener interacts. Use arrival for client-side navigations after that first interaction.

setVolume()

setVolume(volume: number): void

Sets the global level for future playback, clamped between 0 and 1. Non-finite values are ignored.

Your app owns the setting: cuelume applies it but never persists it, so wire it to whatever preference store you already have.

app.ts
import { setVolume } from "cuelume";

setVolume(0.7);
setVolume(Number(localStorage.getItem("ui-volume") ?? 1));

setEnabled()

setEnabled(enabled: boolean): void

Turns future playback on or off. Sounds already ringing are left to finish, and like the volume, the preference is not persisted.

app.ts
import { setEnabled } from "cuelume";

setEnabled(false); // further play attempts become no-ops
setEnabled(true);

sounds

sounds: readonly SoundName[]

Every sound name, in catalog order. Useful for building a picker without hardcoding the list.

app.ts
import { sounds, play } from "cuelume";

sounds.forEach((name) => {
  // one button per cue
});

SoundName

type SoundName = "chime" | "sparkle" | "droplet" | "bloom" | "whisper" | "tick" | "press" | "release" | "toggle" | "success" | "error" | "page" | "loading" | "ready" | "pulse" | "scan" | "arrival"

The union of all seventeen names. Import it as a type to keep your own sound maps honest.

app.ts
import { play, type SoundName } from "cuelume";

const forStatus: Record<"ok" | "bad", SoundName> = {
  ok: "success",
  bad: "error",
};

play(forStatus[status]);

Sound catalog

Seventeen cues, each with its own shape. Press one to hear it.

Behavior

The defaults are opinionated so you do not have to be.

Pointer-aware
Hover requires a fine mouse pointer, so it never fires on touch. Press and release support mouse, touch and pen. Toggle follows native click activation, including the keyboard.
Hover repeat guard
Hover sounds are globally throttled to one every 150ms, so sweeping across a menu stays quiet.
Audible without clipping
One shared boosted output stage keeps cues clear, with native compression protecting overlapping sounds.
One lazy AudioContext
Shared across every sound and created on first use, not on import.
Autoplay-friendly
Suspended audio is resumed where possible, without surfacing an error when a browser blocks it.
SSR-safe
Importing on the server is a no-op.

Anything missing?

Open an issue on GitHub, or point your coding agent at agents.md and it can wire cuelume into a project on its own.