Semantic Sound Effects and Audio Feedback for Web Apps – uisfx

Category: Javascript , Recommended | October 29, 2026
Authorromainsimon
Last UpdateOctober 29, 2026
LicenseMIT
Tags
Views0 views
Semantic Sound Effects and Audio Feedback for Web Apps – uisfx

uisfx (UI SFX) is a dependency-free JavaScript library for adding sound feedback to your web apps.

It has semantic cues for interactions and application states, with interchangeable sound packs that retain the same cue names.

The browser runtime synthesizes sounds through Web Audio. The package also includes MP3 and Ogg assets for direct audio playback.

Features

  • One-shot cues for selection, navigation, editing, feedback, rewards, and commerce.
  • 6 controllable loops for ongoing application states.
  • Per-cue volume, playback rate, cooldown, and retrigger settings.
  • Master volume, mute control, and concurrent voice limits.
  • HTML attributes for click, hover, press, and release sounds.
  • Optional persistent preferences through localStorage or a storage adapter.
  • Cancellable preloading for selected cues.

How To Use It

Installation

npm install uisfx

For a plain browser page, import the ES module from a CDN URL inside a <script type="module"> tag:

import { createUISFX } from 'https://cdn.jsdelivr.net/npm/uisfx/dist/index.js';

Basic Usage

Resume audio during the first user interaction before playing a cue.

<button id="play-sound" type="button">Play success sound</button>
import { createUISFX } from 'uisfx';
const ui = createUISFX({ pack: 'minimal' });
document.querySelector('#play-sound').addEventListener('click', async () => {
  if (await ui.unlock()) ui.play('success');
});

Play And Stop State Loops

The library currently contains 6 loop cues (loading, processing, recording, connecting, scanning, and streaming). Hold the returned playback handle until the corresponding state ends.

<button id="start-preview" type="button">Start recording cue</button>
<button id="stop-preview" type="button">Stop recording cue</button>
let recording;
document.querySelector('#start-preview').addEventListener('click', async () => {
  if (!(await ui.unlock())) return;
  recording?.stop();
  recording = ui.play('recording');
});
document.querySelector('#stop-preview').addEventListener('click', () => {
  recording?.stop();
  recording = undefined;
  ui.play('complete');
});

Change Sound Packs And Save Preferences

Enable preferences to save pack, volume, and enabled settings when their setters run.

const savedUi = createUISFX({
  preferences: { key: 'my-app:audio' },
});
savedUi.setPack('studio');
savedUi.setVolume(0.5);

Bind Sounds With HTML Attributes

bindUISFX() delegates events from the document or a specified element. The optional player property connects the bindings to a player already created by your application.

<a href="/docs" data-uisfx-hover="hover">Documentation</a>
<button type="button" data-uisfx-press="press" data-uisfx-release="release">Hold</button>
<button type="button" data-uisfx="success" data-uisfx-pack="soft">Save changes</button>
import { bindUISFX } from 'uisfx';
const binding = bindUISFX(document, { player: ui });
// On view teardown:
// binding.unbind();
// Destroy ui separately when the application no longer needs it.

Preload And Adjust Playback

preload(cues?, options?) prepares all cues by default. Pass a cue array to select sounds and PreloadOptions.signal (AbortSignal) to cancel the remaining work.

const controller = new AbortController();
void ui.preload(['select', 'success', 'error'], {
  signal: controller.signal,
});
ui.play('reaction', { retrigger: 'overlap' });
ui.play('hover', { cooldownMs: 0 });

Use Packaged Audio Files

The package includes sounds/{pack}/{cue}.mp3 and sounds/{pack}/{cue}.ogg. The uisfx/manifest export contains paths, byte sizes, durations, channel counts, loop flags, default volume, categories, and pack names.

This example uses Vite’s ?url asset import:

<button id="audio-preview" type="button">Preview audio file</button>
import soundUrl from 'uisfx/sounds/soft/success.mp3?url';
document.querySelector('#audio-preview').addEventListener('click', () => {
  void new Audio(soundUrl).play();
});

Configuration Options

createUISFX(options?) takes a UISFXOptions object. bindUISFX(root?, options?) also accepts these options and adds player for reusing an existing instance.

  • pack (PackName, default 'minimal'): Selects the initial sound pack. Saved preference is used when omitted.
  • volume (number, default 1): Master volume from 0 to 1.
  • enabled (boolean, default true): Enables or mutes playback.
  • maxVoices (number, default 8): Simultaneous voices, rounded and clamped from 1 to 32. An older active voice is stopped when the limit is reached.
  • cooldownMs (number, default cue-specific): Global override for cue throttling, clamped from 0 to 10000 milliseconds. Built-in values are typing: 180, hover: 60, focus: 80, progress-step: 80, and volume-change: 60 ms; other cues use 0.
  • preferences (UISFXPreferences, default omitted): Enables saved pack, volume, and enabled settings. {} selects browser localStorage.
  • context (AudioContext, default omitted): Uses an existing audio context. destroy() leaves caller-owned contexts open.

Preferences And Binding Options

  • preferences.key (string, default 'uisfx:preferences'): Preference storage key.
  • preferences.storage (UISFXPreferenceStorage, default browser localStorage): Adapter with getItem(key): string | null and setItem(key, value): void.
  • player (UISFXPlayer, default omitted): Existing player passed only to bindUISFX().

Playback Options

ui.play(cue, options?) accepts these PlayOptions fields:

  • volume (number, default cue’s volume): Sound-specific volume from 0 to 1.
  • loop (boolean, default cue’s loop setting): Overrides repeating playback.
  • playbackRate (number, default 1): Playback speed clamped from 0.25 to 4.
  • retrigger ('restart' | 'overlap' | 'ignore'): Defaults to 'restart' for one-shots and 'ignore' for loops. Restarts, layers, or retains an existing playback of the cue.
  • cooldownMs (number, default inherited from player or cue): Per-call cooldown from 0 to 10000 ms.

Public API Methods

const ui = createUISFX();
ui.unlock();                    // Promise<boolean>; resume the AudioContext.
ui.play('success');             // PlayOptions optional; PlayingSFX | null.
ui.preload(['select']);         // Promise<void>; optional cues and PreloadOptions.
ui.setPack('arcade');           // Select pack and move running loops.
ui.getPack();                   // Current PackName.
ui.setVolume(0.5);              // Set master volume and save if configured.
ui.getVolume();                 // Current master volume.
ui.isEnabled();                 // Current enabled state.
const playback = ui.play('processing');
playback?.stop();               // Stop one playback.
playback?.ended;                // Promise<void> resolved when playback finishes.
ui.stopAll();                   // Stop all active cues.
ui.setEnabled(false);           // Mute and stop active sounds.
ui.destroy();                   // Promise<void>; release owned audio resources.

Binding API

const binding = bindUISFX(document, { player: ui });
binding.player; // UISFXPlayer.
binding.unbind();

Catalog And Synthesis Exports

ExportDescription
CATEGORIESThe 13 category definitions (id, label, description).
CUESCue definitions and playback metadata.
PACKSPack definitions and synthesis settings.
cueNamesArray of the 78 cue IDs.
packNamesArray of 12 pack IDs.
getCue(name)Returns CueDefinition for a CueName; throws for an unknown name.
getPack(name)Returns PackDefinition for a PackName; throws for an unknown name.
getPlaybackMode(name)Returns 'one-shot' or 'loop' for a CueName.
soundUrl(pack, cue, format?)Returns a package-relative sound filename. format is 'mp3' (default) or 'ogg'.
createRecipe(pack, cue)Creates a deterministic SoundRecipe for the pack and cue.
renderRecipe(recipe, sampleRate?)Returns stereo RenderedSound; default sampleRate is 44100.

Exported TypeScript Types

TypeContract
CueNameUnion of the cue IDs in the complete cue list.
PackNameUnion of the 12 pack IDs.
CategoryNameUnion of the 13 category IDs.
PlaybackMode`’one-shot’
UISFXOptionspack?, volume?, enabled?, maxVoices?, cooldownMs?, preferences?, context?.
UISFXPreferenceskey?, storage?.
UISFXPreferenceStoragegetItem(key), setItem(key, value).
PlayOptionsvolume?, loop?, playbackRate?, retrigger?, cooldownMs?.
PreloadOptionssignal?: AbortSignal.
PlayingSFXstop(): void, ended: Promise<void>.
UISFXPlayerAll player methods in the reference above.
BindOptionsUISFXOptions plus player?: UISFXPlayer.
UISFXBindingplayer: UISFXPlayer, unbind(): void.
CueDefinitionname, label, category, description, duration, baseMidi, notes, noise?, transient?, panFrom?, panTo?, loop?, defaultVolume.
PackDefinitionname, label, description, bestFor, color, waveform, pitch, duration, attack, decay, noise, transient, brightness, echo, bitDepth, harmonics, fmRatio?, fmDepth?, elasticity?, paper?, brush?, wood?, chime?.
SoundRecipecue, pack, duration, notes, waveform, harmonics, attack, decay, noise, transient, brightness, echo, bitDepth, panFrom, panTo, loop, defaultVolume, fmRatio, fmDepth, elasticity, paper, brush, wood, chime.
RenderedSoundsampleRate, duration, left: Float32Array, right: Float32Array, peak.

HTML Data Attributes

AttributeBehavior
data-uisfx="cue"Play cue on click.
data-uisfx-pack="pack"Set player pack when clicked alongside data-uisfx.
data-uisfx-hover="cue"Play cue on pointer entry.
data-uisfx-press="cue"Play cue on pointer down.
data-uisfx-release="cue"Play cue on pointer up.

Sound Packs

PackSound character
minimalDry and restrained.
softWarm and rounded.
glassBright and crystalline.
arcadeChunky retro-game tones.
mechanicalSwitches and tactile clicks.
organicWood, water, and natural textures.
dreamyAiry textures and soft sparkle.
scifiDigital pings and shimmer.
rubberElastic taps and bounces.
cinematicDeep impacts and sustained tails.
studioRestrained sounds for editing controls.
zenPaper, wood, brush, and quiet chimes.

Complete Semantic Cue List

CategoryCue names
Inputhover, press, release, double-click, focus, long-press
Selectionselect, deselect, toggle-on, toggle-off, check, uncheck
Navigationopen, close, back, forward, expand, collapse
Editingdelete, cancel, undo, redo, copy, paste
Movementdrag-start, drop, snap, swipe, reorder, invalid-drop
Communicationsend, receive, notification, mention, typing, reaction
Feedbacksuccess, error, warning, info, blocked, retry
Progressstart, stop, progress-step, complete, queued, checkpoint
Loopsloading, processing, recording, connecting, scanning, streaming
Mediaplay, pause, seek, volume-change, skip-next, skip-previous
Systemconnect, disconnect, lock, unlock, wake, sleep
Rewardreward, level-up, achievement, streak, badge, bonus
Commerceadd-to-cart, remove-from-cart, checkout, purchase, coupon, refund

Alternatives

FAQs

Q: Why is there no sound on the first interaction?
A: Browsers can suspend AudioContext until a user gesture. Call await ui.unlock() in the first pointer or keyboard handler.

Q: Will HTML bindings work for elements inserted later?
A: Yes. bindUISFX() delegates its event listeners from the configured root.

Q: Can I use uisfx with server-side rendering?
A: Initialize playback on the client. bindUISFX() requires a browser document or element, and sound playback uses AudioContext.

You Might Be Interested In:


Leave a Reply