First public release of seslen — a zero-dependency Web Audio library that ships with 36 synthesised UI sound presets and a high-DX API on top of AudioContext. No audio files, no network, no decode: every preset is generated at call time from oscillators + envelopes.
import { createSeslen } from "seslen"
import { presets, presetDefaults } from "seslen/presets"
const ses = createSeslen({
sources: presets,
defaults: presetDefaults,
persist: "seslen:master",
})
await ses.play("victory")What's in the box
Core API
createSeslen({ sources, defaults, volume, buses, maxVoices, respectReducedMotion, persist })play(name, opts?)— returnsPromise<PlayHandle | null>(null when throttled / dropped)playPattern(steps)— schedule a timed sequencepreload,stop(name),stopAll,register,unregister,has,names- Master:
getVolume,setVolume,mute,unmute,isMuted - Lifecycle:
pause,resume,close,isReady,state - Events:
play,ended,throttled,statechange
Web Audio surface
- 🎛 Per-call options —
gain,rate,detune,loop,pan,fadeIn,fadeOut,when,sprite,interrupt - 🌀 Jitter —
rateJitter/gainJitter/detuneJitterso 100 ticks don't sound like 1 tick repeated - 🚦 Throttle per call — drop rapid-fire repeats inside a window
- 🎚 Polyphony cap — per-source
voices+steal: "oldest" | "newest" | "drop" - 🚌 Buses — named sub-mixers (
ses.bus("music")) with their ownvolume/muteand ducking (sidechain) - ⏱ Sample-accurate scheduling —
play(name, { when: ses.now() + 0.25 }) - 🪄 PlayHandle —
stop,done,duration,onEnded,fadeTo,setGain,rampRate - 📼 OfflineAudioContext render-to-WAV —
await ses.render("victory")returns aBlob - 📈 AnalyserNode tap —
ses.analyser({ fftSize })for waveform / spectrum data
Accessibility, persistence, SSR
- ♿️ Honours
prefers-reduced-motion: reduceby auto-muting (opt-out viarespectReducedMotion: false) - 💾
localStoragepersistence for master volume + mute (opt-in viapersist: "key") - 🛡 SSR-safe stub at
seslen/server— every method is a typed no-op - 🔓 Auto-unlocks the
AudioContexton the first user gesture
36 built-in presets
| Group | Presets |
|---|---|
| Original eight | tick, success, error, warning, message, add, delete, victory |
| UI feedback | hover, pop, swoosh, toggle-on, toggle-off, notify, keypress, scroll-tick, drag, drop, expand, collapse, undo, redo, send, receive, copy, paste |
| Game / playful | level-up, coin, jump, shoot, explosion |
| Ambient / state | heartbeat, alarm, typewriter, lock, unlock |
Every preset ships its own metadata (label, description, tags, recipe, durationMs, motion, accent, defaults) so picker UIs and docs can introspect the library directly.
Quality
- Zero runtime dependencies
- Pure ESM, tree-shakeable
- Strict TypeScript (
tsgo --noEmitclean,verbatimModuleSyntax,isolatedModules) - Vitest 56/56 green — separate suites for buses, voices, throttle, jitter, persist, render, presets, registry, cache, server stub and the browser integration
- Bundle budget enforced — core 12 KB raw, presets/index 8 KB, preset entry 2 KB, server 2 KB (gzip ≈ ⅓)
Playground
web/ ships a Vite + React + Tailwind v4 + Tanstack Router composer where you can drop preset blocks onto lanes, edit timing/intensity per block, share patterns via URL, render the snippet with syntax-highlighted code via @comark/react, and preview the waveform live.
Install
npm install seslenWhat's next (v0.2.0)
- Framework adapters:
seslen/react,seslen/vue,seslen/svelte - Theme / preset packs (shadcn-style installable preset bundles)
- Per-instance effect chain (filter / reverb send) + built-in reverb / compressor