Core

Sound Roles

All 17 semantic sound roles across 5 categories with TypeScript types and role registry.

sensory-ui defines 17 semantic sound roles organized into 5 categories. Each role maps to a specific UI interaction type. No random audio, every sound has meaning.


Category Overview

CategoryCountDefault EnabledDuration RangeCharacter
interaction4Yes8–50 msShort transients, soft attack
overlay4Yes120–270 msTonal pairs (open ↔ close)
navigation3Yes40–250 msLow-volume sweeps, directional
notification4Yes200–450 msTwo-tier intensity
hero2No350–1900 msCelebratory, longer form

Durations are base tune lengths: the active pack's instrument stretches them via its decay multiplier, and hero sounds are hand-crafted per pack so they vary the most.


Category: interaction

Fired when the user performs a direct action: pressing a button, toggling a state, confirming a form, or providing subtle feedback during continuous interactions.

RoleFull KeyDurationDescriptionTypical Trigger
Tapinteraction.tap~8 msDefault click/tap sound. Noise click, bandpass 3800 Hz.Primary button click
Subtleinteraction.subtle~8 msKeyboard click. Crisp bandpass ~3600 Hz, tighter Q.Slider drag, command input keypress
Toggleinteraction.toggle~35 msFM pitch-glide pop (700 → 480 Hz). State change.Checkbox, switch, radio, toggle
Confirminteraction.confirm~12 msCrisp noise click, bandpass 5500 Hz. Brighter than tap.Form submit, save confirm

There is no interaction.disabled role because disabled buttons cannot be clicked, so no sound is needed.


Category: overlay

Fired when UI surfaces open, close, expand, or collapse. These are structural changes to the visual layout.

RoleFull KeyDurationDescriptionTypical Trigger
Openoverlay.open~180 msA gentle expansion sound.Dialog open, sheet open, dropdown open
Closeoverlay.close~180 msTonal inverse of open. Collapse.Dialog close, sheet close, dropdown close
Expandoverlay.expand~120 msLighter than open. Content reveals.Accordion expand, collapsible open
Collapseoverlay.collapse~120 msPaired with expand. Content hides.Accordion collapse, collapsible close

overlay.open / overlay.close are a tonal inversion pair: if open rises in pitch, close falls. The same applies to expand/collapse.


Category: navigation

Fired when the user moves through space: between pages, tabs, or steps.

RoleFull KeyDurationDescriptionTypical Trigger
Forwardnavigation.forward~140 msA rightward / upward sweep.Next step, next page, carousel next
Backwardnavigation.backward~140 msA leftward / downward sweep. Tonal mirror of forward.Back button, previous step, carousel prev
Tabnavigation.tab~40 msShort tonal pop with upward pitch bend.Tab switch, segment switch, pagination

navigation.forward and navigation.backward should be tonal inversions of each other (same harmonic content, opposite pitch arc).


Category: notification

Fired when the system delivers feedback to the user: toasts, alerts, banners, inline validation messages.

RoleFull KeyDurationDescriptionTypical Trigger
Infonotification.info~220 msNeutral information. Soft single chime.Info toast, passive alert
Successnotification.success~450 msThree ascending notes, C5–E5–G5 (positive feeling).Success toast, form saved
Warningnotification.warning~350 msTwo-note semitone ascent, A4 → A♯4 (attention-getting).Quota alert, confirmation needed
Errornotification.error~200 msFiltered descending sweep, 320 → 140 Hz (negative connotation).Error toast, connection failed

Category: hero

Fired for significant completion moments: task completion, onboarding milestones, achievement unlocks. These are special-occasion sounds and are disabled by default.

RoleFull KeyDurationDescriptionTypical Trigger
Completehero.complete~650–1900 msTask done. Multi-note fanfare, hand-crafted per pack.Checklist complete, upload done
Milestonehero.milestone~350–1100 msLighter celebration. Shorter than complete.Onboarding step done, streak saved

Hero sounds are disabled by default. You must explicitly enable the category (config={{ categories: { hero: true } }}) and then assign the sound prop, a double opt-in that prevents accidental hero sounds in production.


TypeScript Types

// components/ui/sensory-ui/config/sound-roles.ts

export type SoundCategory =
  | "interaction"
  | "overlay"
  | "navigation"
  | "notification"
  | "hero";

export type InteractionRole =
  | "interaction.tap"
  | "interaction.subtle"
  | "interaction.toggle"
  | "interaction.confirm";

export type OverlayRole =
  | "overlay.open"
  | "overlay.close"
  | "overlay.expand"
  | "overlay.collapse";

export type NavigationRole =
  | "navigation.forward"
  | "navigation.backward"
  | "navigation.tab";

export type NotificationRole =
  | "notification.info"
  | "notification.success"
  | "notification.warning"
  | "notification.error";

export type HeroRole = "hero.complete" | "hero.milestone";

export type SoundRole =
  | InteractionRole
  | OverlayRole
  | NavigationRole
  | NotificationRole
  | HeroRole;

Role Parser

export function parseRole(role: SoundRole): {
  category: SoundCategory;
  name: string;
} {
  const [category, name] = role.split(".") as [SoundCategory, string];
  return { category, name };
}

All Valid Roles

export const ALL_SOUND_ROLES: SoundRole[] = [
  "interaction.tap",
  "interaction.subtle",
  "interaction.toggle",
  "interaction.confirm",
  "overlay.open",
  "overlay.close",
  "overlay.expand",
  "overlay.collapse",
  "navigation.forward",
  "navigation.backward",
  "navigation.tab",
  "notification.info",
  "notification.success",
  "notification.warning",
  "notification.error",
  "hero.complete",
  "hero.milestone",
];

Role Registry

The role registry maps each SoundRole string to a SoundSynthesizer function within a sound pack:

// components/ui/sensory-ui/config/registry.ts

import type { SoundRole } from "./sound-roles";
import type { SoundSource } from "./engine";
// SoundPackName is defined in sounds/packs.ts as
// `keyof typeof soundPacks` and re-exported here:
import type { SoundPackName } from "../sounds/packs";

export type SoundPack = Record<SoundRole, SoundSource>;

export const packRegistry: Record<SoundPackName, SoundPack> = {
  soft: softPack,
  aero: aeroPack,
  arcade: arcadePack,
  organic: organicPack,
  glass: glassPack,
  industrial: industrialPack,
  minimal: minimalPack,
  retro: retroPack,
  crisp: crispPack,
};

export const DEFAULT_PACK: SoundPackName = "aero";

Audio Specifications for Custom Overrides

When providing custom audio source overrides via config.overrides, files should meet these requirements:

RequirementSpec
Format.mp3 (primary): maximum browser compatibility
Fallback format.ogg (optional)
Sample rate44.1 kHz or 48 kHz
Bit depth16-bit
ChannelsMono (L+R identical) preferred
Peak normalization−1.0 dBTP (true peak)
Leading silenceNone: trimmed to first transient
Trailing silence≤ 20 ms tail, then trimmed
Per-category size budget≤ 50 KB total per category
Per-file size guideline≤ 15 KB per file

MP3 is preferred because decodeAudioData supports it universally across modern browsers. WAV files are larger with no Web Audio benefit.