Library / SDK
romainsimon/uisfx avatar
romainsimon/uisfx

UI SFX: A Semantic Sound System for Web and Native Interfaces

UI Sound Effects for your interfaces

835 stars45 forksVueMIT

At a glance

What is it?
UI SFX is a 12.0 kB Web Audio library that maps 78 named interaction cues to 12 curated sound packs, letting teams swap the full sonic character of a product by changing one parameter. It ships portable MP3 and Ogg files for React Native, Unity, Godot, and Swift alongside its browser runtime.
Who is it for?
UI SFX fits web apps and SaaS products where audio feedback reinforces visible state changes without creating accessibility dependencies. The MIT runtime and CC0 audio make it usable in commercial products without additional licensing steps.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 23 days ago.
What is it written in?
Mainly Vue, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Naming Sounds by Intent, Not by Filename

Most projects that add UI audio end up with files named things like soft-pop-03.mp3 and then scatter the actual meanings across component code. UI SFX separates intent from timbre through a semantic layer.

The project defines 78 named cues across 13 interaction categories. Names like success, drop, warning, and level-up are stable in application code regardless of which sound pack is active. The mapping works like this:

text
product event  ->  semantic cue  ->  sound pack  ->  recipe or asset
upload done        complete          glass           complete.ogg
lesson done        complete          arcade          complete.ogg

A team can move from the minimal pack to the arcade pack by calling ui.setPack('arcade'), and every interaction site picks up the new sounds without change. That contract is documented in the project's taxonomy file and applies to all twelve packs. Each pack implements every one of the 78 cues, so switching personalities never leaves a cue silent.

A 12.0 kB Web Audio Runtime Without Dependencies

The browser runtime weighs 12.0 kB compressed and carries no external dependencies. It does not fetch audio files at playback time. Instead, it synthesizes deterministic recipes locally when a cue is first played and caches the rendered buffers in memory.

The AudioContext is created lazily, which avoids the NotAllowedError that browsers throw when code tries to start audio before user interaction. The player defaults to eight simultaneous voices, deduplicates concurrent loops (a second call to ui.play('loading') returns the existing handle rather than spawning a second one), restarts repeated outcomes so a double-click does not stack two success sounds, and rate-limits high-frequency cues. When setPack is called mid-session, active loops migrate into the new pack without their handles being invalidated.

For projects that cannot use Web Audio, the package also ships 936 original audio files in both MP3 (5.18 MB total) and higher-fidelity Ogg (3.82 MB total) under sounds/{pack}/{cue}.mp3 and sounds/{pack}/{cue}.ogg.

Installing UI SFX and Unlocking the AudioContext

Install the package from npm:

bash
npm install uisfx

Create a player instance with a starting pack, call unlock() from the first trusted pointer or keyboard event, then call play() with a cue name:

ts
import { createUISFX } from 'uisfx'

const ui = createUISFX({ pack: 'minimal', preferences: {} })

// Call from the first trusted pointer or keyboard action.
await ui.unlock()

saveButton.addEventListener('click', () => {
  ui.play('success')
})

Passing preferences: {} tells the library to persist the selected pack, volume level, and enabled state in localStorage. A custom storage adapter can replace localStorage for React Native shells or application preference stores. The preload() method yields between cues and accepts an AbortSignal so it does not monopolize a browser task when warming up sounds before they are needed.

The twelve packs cover a wide range of product characters. The minimal pack is designed for SaaS and productivity tools, glass for premium media and finance, arcade for gamified learning, and zen for calm and focus tools. The full table of packs with their intended fits is in the README.

One-Shots for Discrete Outcomes, Loops for Ongoing States

UI SFX distinguishes between two cue types. One-shots are short sounds for discrete events: selections, drops, purchases, success confirmations, warnings, and errors. The library ships 72 of them, each kept brief to avoid slowing perceived interaction momentum.

Loops are for visible ongoing states. The six loop cues are loading, processing, recording, connecting, scanning, and streaming. A loop continues playing until its stop() method is called. The README shows the typical pattern:

ts
const recording = ui.play('recording')

stopButton.addEventListener('click', () => {
  recording?.stop()
  ui.play('complete')
})

The player deduplicates loops, so calling play('processing') twice returns one shared handle rather than layering two identical tracks. Stop loops as soon as the underlying state resolves (success, failure, or cancellation), not after a delay, since an orphaned loop is audible to users in a way that an orphaned visual state generally is not.

HTML Bindings and Accessibility Controls

For projects that prefer a declarative approach over imperative calls, UI SFX provides bindUISFX(), which attaches a global event listener and reads data-uisfx attributes:

ts
import { bindUISFX } from 'uisfx'

const { player, unbind } = bindUISFX()
html
<a data-uisfx-hover="hover">Documentation</a>
<button data-uisfx-press="press" data-uisfx-release="release">Hold me</button>
<button data-uisfx="success" data-uisfx-pack="soft">Save</button>

Call unbind() when a client-rendered view unmounts. The attribute data-uisfx-pack on a single element overrides the global pack, so individual components can use a different personality without changing the site-wide setting.

The README documents explicit accessibility requirements: sound must reinforce visible feedback, never replace it. Users must have a persistent mute setting. Audio must never be the only distinction between success, warning, and error states. The control surface is small: setEnabled(false) mutes all output, setVolume(0.5) scales it, and stopAll() silences every active cue at once.

ts
ui.setEnabled(false)
ui.setVolume(0.5)
ui.stopAll()

Hover cues should be kept quiet or disabled in dense interfaces to avoid continuous noise during normal cursor movement.

Portable Audio Files for React Native, Unity, and Godot

The browser runtime is not the only delivery target. The package includes sounds/{pack}/{cue}.mp3 and sounds/{pack}/{cue}.ogg for environments without Web Audio: React Native, Swift, Kotlin, Unity, Godot, and video production.

Importing a specific file through a bundler that supports the ?url query pattern returns the resolved asset path:

ts
import successUrl from 'uisfx/sounds/soft/success.mp3?url'

const success = new Audio(successUrl)
await success.play()

The uisfx/manifest export provides a machine-readable index of every file with its exact path, byte size, rendered duration, channel count, loop flag, default volume, cue name, category, and pack. This makes it possible to build a custom audio system on top of the files without hardcoding paths.

All 936 sounds and the sound-pack artwork are CC0. The JavaScript runtime is MIT. Both are usable in commercial products without attribution requirements.

Build Requirements, Limitations, and What UI SFX Does Not Cover

Regenerating sounds from source requires Node 22.20 or later, a Chrome or Chromium binary, and ffmpeg. The full quality gate runs synthesis, type checking, tests, a browser conformance check, and audio validation covering decoded peaks, tail silence, loop timing, seam continuity, and pairwise similarity. Developers who only consume the published npm package face none of these requirements.

The library covers interface feedback only. It has no facilities for background music, adaptive audio, dynamic mixing, or narrative cues. Projects that need those capabilities require a different tool. Howler.js, for example, is a general-purpose JavaScript audio library that handles arbitrary audio file playback and includes sprite support and spatial audio, but it provides no semantic naming layer; every product event must be mapped to a file path in application code, and swapping the sonic style of a product means updating those mappings everywhere.

Browser autoplay policies remain a hard constraint. Calling play() before unlock() may produce silence in Chrome, Safari, or Firefox with no error thrown. The README states that unlock() must be called from the first trusted interaction. The library creates its AudioContext lazily, but that laziness does not bypass the autoplay gate; it only defers AudioContext creation until unlock() runs.

The repository has no GitHub releases. The last push was on 2026-09-08. The project ships an AGENTS.md file and a production-ready agent prompt at uisfx.com/agent-prompt.txt intended for coding agents that wire UI SFX into a product; the prompt covers semantic cue mapping, loop cleanup, autoplay constraints, mute preferences, accessibility requirements, and verification steps.

Editorial conclusion

UI SFX fits web apps and SaaS products where audio feedback reinforces visible state changes without creating accessibility dependencies. The MIT runtime and CC0 audio make it usable in commercial products without additional licensing steps. It is the wrong tool if you need background music, narrative audio, or a fully programmable sound engine. Before going to production, call unlock() from the first trusted user interaction and verify that your deployment passes autoplay constraints in the browsers your users rely on.

Frequently asked questions

How does UI SFX handle browser autoplay restrictions?

UI SFX creates its AudioContext lazily. Call unlock() from the first trusted pointer or keyboard interaction and all subsequent cues play without autoplay errors. Calling play() before unlock() may produce silence in Chrome, Safari, and Firefox without throwing an error.

Can UI SFX be used outside the browser in React Native or game engines?

The package ships portable MP3 and Ogg files at sounds/{pack}/{cue}.mp3 and sounds/{pack}/{cue}.ogg for React Native, Swift, Kotlin, Unity, Godot, and video production. The uisfx/manifest export lists the exact path, byte size, duration, and channel count for each of the 936 files.

What licenses cover the UI SFX code and audio?

The JavaScript runtime is MIT licensed. All 936 audio files and the sound-pack artwork are CC0, making them usable in commercial products without attribution. The LICENSE and LICENSE-AUDIO files in the repository document both separately.

Official sources

  1. Issues
  2. License: MIT
  3. Project website
  4. README
  5. romainsimon/uisfx on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/romainsimon-uisfx.svg)](https://hysenlabs.com/projects/romainsimon-uisfx)