websfx
Version:
Play synthesized UI sound effects using the Web Audio API
1 lines • 24.7 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","names":[],"sources":["../src/utils.ts","../src/context.ts","../src/engine.ts","../src/sounds.ts"],"sourcesContent":["/**\n * Clamps a number to the inclusive range 0-1.\n *\n * @param value - The number to clamp.\n * @returns The clamped value.\n */\nexport function clamp(value: number): number {\n return Math.min(1, Math.max(0, value));\n}\n","import type { Config } from './types';\nimport { clamp } from './utils';\n\n/**\n * The shared audio graph that all sounds play through.\n */\ninterface AudioIO {\n context: AudioContext;\n master: GainNode;\n}\n\nlet audio: AudioIO | undefined;\nlet config: Config = { volume: 1 };\n\n/**\n * Returns the shared audio graph, creating the `AudioContext` lazily on\n * first use.\n *\n * @returns The audio graph, or `undefined` when Web Audio is unavailable\n * (for example during server-side rendering) or when the master volume\n * is `0`.\n */\nexport function getAudio(): AudioIO | undefined {\n if (config.volume <= 0) return undefined;\n const io = ensureAudio();\n if (io?.context.state === 'suspended') {\n void io.context.resume().catch(() => undefined);\n }\n return io;\n}\n\n/**\n * Reads or updates the global sound configuration.\n *\n * Called with a partial configuration it applies the update first. Always\n * returns the current configuration. Set `volume` to `0` to mute.\n *\n * @example\n * ```ts\n * configure({ volume: 0.5 });\n * configure({ volume: 0 }); // mute\n * const { volume } = configure();\n * ```\n *\n * @param options - Partial configuration to merge.\n * @returns The current configuration.\n */\nexport function configure(options?: Partial<Config>): Readonly<Config> {\n if (options?.volume !== undefined) {\n config = { volume: clamp(options.volume) };\n if (audio) {\n audio.master.gain.value = config.volume;\n }\n }\n return { ...config };\n}\n\n/**\n * Creates the `AudioContext` and master gain node on first use.\n *\n * @returns The audio graph, or `undefined` when Web Audio is unavailable.\n */\nfunction ensureAudio(): AudioIO | undefined {\n if (audio) return audio;\n if (typeof AudioContext === 'undefined') return undefined;\n let context: AudioContext;\n try {\n context = new AudioContext();\n } catch {\n return undefined;\n }\n const master = context.createGain();\n master.gain.value = config.volume;\n master.connect(context.destination);\n audio = { context, master };\n return audio;\n}\n\n/**\n * Clears the memoized `AudioContext` and restores the default\n * configuration.\n *\n * @internal\n */\nexport function reset(): void {\n audio = undefined;\n config = { volume: 1 };\n}\n","import { getAudio } from './context';\nimport type { SoundOptions } from './types';\nimport { clamp } from './utils';\n\n/**\n * Floor for exponential gain ramps; `0` is not a valid ramp target.\n */\nconst FLOOR = 0.0001;\n\n/**\n * Options for a single oscillator tone.\n */\nexport interface ToneOptions extends SoundOptions {\n /**\n * Frequency of the tone in hertz.\n */\n frequency: number;\n\n /**\n * Frequency the tone sweeps to over its duration.\n */\n endFrequency?: number;\n\n /**\n * Duration of the tone in seconds.\n *\n * @defaultValue 0.1\n */\n duration?: number;\n\n /**\n * Delay from the current time before the tone starts, in seconds.\n *\n * @defaultValue 0\n */\n delay?: number;\n\n /**\n * Time in seconds to reach full volume.\n *\n * @defaultValue 0.005\n */\n attack?: number;\n\n /**\n * Oscillator waveform.\n *\n * @defaultValue 'sine'\n */\n type?: OscillatorType;\n}\n\n/**\n * Options for a filtered noise burst.\n */\nexport interface NoiseOptions extends SoundOptions {\n /**\n * Duration of the burst in seconds.\n *\n * @defaultValue 0.1\n */\n duration?: number;\n\n /**\n * Delay from the current time before the burst starts, in seconds.\n *\n * @defaultValue 0\n */\n delay?: number;\n\n /**\n * Time in seconds to reach full volume.\n *\n * @defaultValue 0.002\n */\n attack?: number;\n\n /**\n * Filter type.\n *\n * @defaultValue 'lowpass'\n */\n type?: BiquadFilterType;\n\n /**\n * Filter cutoff frequency in hertz, scaled by `pitch`.\n *\n * @defaultValue 1500\n */\n frequency?: number;\n\n /**\n * Filter resonance.\n */\n q?: number;\n}\n\n/**\n * A single note in a sequence.\n */\nexport interface Note {\n /**\n * Frequency of the note in hertz.\n */\n frequency: number;\n\n /**\n * Duration of the note in seconds.\n *\n * @defaultValue 0.08\n */\n duration?: number;\n}\n\n/**\n * Options for a sequence of notes.\n */\nexport interface SequenceOptions extends SoundOptions {\n /**\n * Oscillator waveform applied to every note.\n *\n * @defaultValue 'sine'\n */\n type?: OscillatorType;\n\n /**\n * Silence between notes in seconds.\n *\n * @defaultValue 0.02\n */\n gap?: number;\n}\n\n/**\n * Plays a single oscillator tone through the master gain.\n *\n * Does nothing when Web Audio is unavailable, or when the requested\n * volume, duration, or frequency is not positive.\n *\n * @param options - Tone options.\n */\nexport function tone(options: ToneOptions): void {\n const audio = getAudio();\n if (!audio) return;\n const {\n frequency,\n endFrequency,\n duration = 0.1,\n delay = 0,\n attack = 0.005,\n type = 'sine',\n volume = 1,\n pitch = 1,\n } = options;\n const peak = clamp(volume);\n const startFrequency = frequency * pitch;\n if (duration <= 0 || peak <= 0 || startFrequency <= 0) return;\n const { context, master } = audio;\n const start = context.currentTime + delay;\n const end = start + duration;\n const oscillator = context.createOscillator();\n const gain = context.createGain();\n oscillator.type = type;\n oscillator.frequency.setValueAtTime(startFrequency, start);\n if (endFrequency !== undefined) {\n oscillator.frequency.exponentialRampToValueAtTime(\n Math.max(1, endFrequency * pitch),\n end,\n );\n }\n gain.gain.setValueAtTime(FLOOR, start);\n gain.gain.exponentialRampToValueAtTime(\n peak,\n start + Math.min(attack, duration),\n );\n gain.gain.exponentialRampToValueAtTime(FLOOR, end);\n oscillator.connect(gain);\n gain.connect(master);\n oscillator.start(start);\n oscillator.stop(end);\n}\n\n/**\n * Plays a filtered burst of white noise through the master gain.\n *\n * The noise buffer is generated once per `AudioContext` and reused.\n *\n * @param options - Noise options.\n */\nexport function noise(options: NoiseOptions = {}): void {\n const audio = getAudio();\n if (!audio) return;\n const {\n duration = 0.1,\n delay = 0,\n attack = 0.002,\n type = 'lowpass',\n frequency = 1500,\n q,\n volume = 1,\n pitch = 1,\n } = options;\n const peak = clamp(volume);\n if (duration <= 0 || peak <= 0) return;\n const { context, master } = audio;\n const start = context.currentTime + delay;\n const end = start + duration;\n const source = context.createBufferSource();\n const filter = context.createBiquadFilter();\n const gain = context.createGain();\n source.buffer = getNoiseBuffer(context);\n filter.type = type;\n filter.frequency.setValueAtTime(Math.max(1, frequency * pitch), start);\n if (q !== undefined) {\n filter.Q.value = q;\n }\n gain.gain.setValueAtTime(FLOOR, start);\n gain.gain.exponentialRampToValueAtTime(\n peak,\n start + Math.min(attack, duration),\n );\n gain.gain.exponentialRampToValueAtTime(FLOOR, end);\n source.connect(filter);\n filter.connect(gain);\n gain.connect(master);\n source.start(start);\n source.stop(end);\n}\n\n/**\n * Plays a series of tones, one after another.\n *\n * @param notes - The notes to play.\n * @param options - Sequence options applied to every note.\n */\nexport function sequence(notes: Note[], options: SequenceOptions = {}): void {\n const { gap = 0.02, type = 'sine', ...rest } = options;\n let delay = 0;\n for (const note of notes) {\n const duration = note.duration ?? 0.08;\n tone({ frequency: note.frequency, duration, delay, type, ...rest });\n delay += duration + gap;\n }\n}\n\nconst noiseBuffers = new WeakMap<AudioContext, AudioBuffer>();\n\n/**\n * Returns a one-second white noise buffer for the given context,\n * creating and memoizing it on first use.\n *\n * @param context - The context to create the buffer for.\n * @returns The noise buffer.\n */\nfunction getNoiseBuffer(context: AudioContext): AudioBuffer {\n const cached = noiseBuffers.get(context);\n if (cached) return cached;\n const length = Math.floor(context.sampleRate);\n const buffer = context.createBuffer(1, length, context.sampleRate);\n const data = buffer.getChannelData(0);\n for (let index = 0; index < length; index += 1) {\n data[index] = Math.random() * 2 - 1;\n }\n noiseBuffers.set(context, buffer);\n return buffer;\n}\n","import { noise, sequence, tone, type ToneOptions } from './engine';\nimport type { BeepOptions, SoundOptions } from './types';\n\nconst clickTone: ToneOptions = {\n frequency: 1000,\n endFrequency: 700,\n duration: 0.05,\n type: 'triangle',\n volume: 0.8,\n};\n\nfunction playClick(options: SoundOptions, delay = 0): void {\n tone({ ...clickTone, ...options, delay });\n}\n\n/**\n * Plays a sharp click, e.g. for buttons and links.\n *\n * @param options - Per-call sound options.\n */\nexport function click(options: SoundOptions = {}): void {\n playClick(options);\n}\n\n/**\n * Plays a soft tick for pointer hover states.\n *\n * @param options - Per-call sound options.\n */\nexport function hover(options: SoundOptions = {}): void {\n tone({\n frequency: 900,\n duration: 0.04,\n type: 'sine',\n volume: 0.4,\n ...options,\n });\n}\n\n/**\n * Plays a subtle cue for keyboard focus.\n *\n * @param options - Per-call sound options.\n */\nexport function focus(options: SoundOptions = {}): void {\n tone({\n frequency: 1200,\n duration: 0.05,\n type: 'sine',\n volume: 0.5,\n ...options,\n });\n}\n\n/**\n * Plays a descending tone for a pointer or key press.\n *\n * @param options - Per-call sound options.\n */\nexport function press(options: SoundOptions = {}): void {\n tone({\n frequency: 700,\n endFrequency: 500,\n duration: 0.06,\n type: 'triangle',\n volume: 0.7,\n ...options,\n });\n}\n\n/**\n * Plays a rising tone for a pointer or key release.\n *\n * @param options - Per-call sound options.\n */\nexport function release(options: SoundOptions = {}): void {\n tone({\n frequency: 500,\n endFrequency: 750,\n duration: 0.06,\n type: 'triangle',\n volume: 0.7,\n ...options,\n });\n}\n\n/**\n * Plays a sustained rising tone for long press gestures.\n *\n * @param options - Per-call sound options.\n */\nexport function longPress(options: SoundOptions = {}): void {\n tone({\n frequency: 440,\n endFrequency: 660,\n duration: 0.3,\n attack: 0.05,\n type: 'sine',\n volume: 0.7,\n ...options,\n });\n}\n\n/**\n * Plays two quick clicks for double click gestures.\n *\n * @param options - Per-call sound options.\n */\nexport function doubleClick(options: SoundOptions = {}): void {\n playClick(options);\n playClick(options, 0.09);\n}\n\n/**\n * Plays a soft friction tick for drag gestures.\n *\n * @param options - Per-call sound options.\n */\nexport function drag(options: SoundOptions = {}): void {\n noise({\n duration: 0.1,\n type: 'bandpass',\n frequency: 1200,\n q: 2,\n volume: 0.4,\n ...options,\n });\n}\n\n/**\n * Plays a low thud for drop gestures.\n *\n * @param options - Per-call sound options.\n */\nexport function drop(options: SoundOptions = {}): void {\n tone({\n frequency: 150,\n endFrequency: 60,\n duration: 0.18,\n type: 'sine',\n volume: 0.9,\n ...options,\n });\n noise({\n duration: 0.12,\n type: 'lowpass',\n frequency: 500,\n volume: 0.3,\n ...options,\n });\n}\n\n/**\n * Plays a keystroke tap for text input.\n *\n * @param options - Per-call sound options.\n */\nexport function type(options: SoundOptions = {}): void {\n noise({\n duration: 0.03,\n type: 'bandpass',\n frequency: 2500,\n q: 1,\n volume: 0.5,\n ...options,\n });\n}\n\n/**\n * Plays a rising two-note chime for selection.\n *\n * @param options - Per-call sound options.\n */\nexport function select(options: SoundOptions = {}): void {\n sequence(\n [\n { frequency: 660, duration: 0.06 },\n { frequency: 990, duration: 0.09 },\n ],\n { type: 'sine', gap: 0, volume: 0.65, ...options },\n );\n}\n\n/**\n * Plays a falling two-note chime for deselection.\n *\n * @param options - Per-call sound options.\n */\nexport function deselect(options: SoundOptions = {}): void {\n sequence(\n [\n { frequency: 990, duration: 0.06 },\n { frequency: 660, duration: 0.09 },\n ],\n { type: 'sine', gap: 0, volume: 0.65, ...options },\n );\n}\n\n/**\n * Plays a rising sweep for opening UI such as modals and popovers.\n *\n * @param options - Per-call sound options.\n */\nexport function open(options: SoundOptions = {}): void {\n tone({\n frequency: 300,\n endFrequency: 900,\n duration: 0.18,\n type: 'triangle',\n volume: 0.65,\n ...options,\n });\n}\n\n/**\n * Plays a falling sweep for closing UI such as modals and popovers.\n *\n * @param options - Per-call sound options.\n */\nexport function close(options: SoundOptions = {}): void {\n tone({\n frequency: 900,\n endFrequency: 300,\n duration: 0.18,\n type: 'triangle',\n volume: 0.65,\n ...options,\n });\n}\n\n/**\n * Plays a falling swoosh for navigating back.\n *\n * @param options - Per-call sound options.\n */\nexport function back(options: SoundOptions = {}): void {\n tone({\n frequency: 700,\n endFrequency: 350,\n duration: 0.15,\n type: 'sine',\n volume: 0.65,\n ...options,\n });\n}\n\n/**\n * Plays a rising swoosh for navigating forward.\n *\n * @param options - Per-call sound options.\n */\nexport function forward(options: SoundOptions = {}): void {\n tone({\n frequency: 350,\n endFrequency: 700,\n duration: 0.15,\n type: 'sine',\n volume: 0.65,\n ...options,\n });\n}\n\n/**\n * Plays a plain sine beep.\n *\n * @example\n * ```ts\n * beep();\n * beep({ frequency: 440 });\n * ```\n *\n * @param options - Per-call sound options and tone frequency.\n */\nexport function beep(options: BeepOptions = {}): void {\n const { frequency = 880, ...rest } = options;\n tone({ frequency, duration: 0.15, type: 'sine', volume: 0.7, ...rest });\n}\n\n/**\n * Plays a rising major arpeggio for successful operations.\n *\n * @param options - Per-call sound options.\n */\nexport function success(options: SoundOptions = {}): void {\n sequence(\n [\n { frequency: 523.25, duration: 0.08 },\n { frequency: 659.25, duration: 0.08 },\n { frequency: 783.99, duration: 0.08 },\n { frequency: 1046.5, duration: 0.16 },\n ],\n { type: 'sine', gap: 0.02, volume: 0.65, ...options },\n );\n}\n\n/**\n * Plays a low two-note buzz for failed operations.\n *\n * @param options - Per-call sound options.\n */\nexport function error(options: SoundOptions = {}): void {\n sequence(\n [\n { frequency: 220, duration: 0.12 },\n { frequency: 165, duration: 0.18 },\n ],\n { type: 'square', gap: 0, volume: 0.45, ...options },\n );\n}\n\n/**\n * Plays an alternating two-tone alert for warnings.\n *\n * @param options - Per-call sound options.\n */\nexport function warning(options: SoundOptions = {}): void {\n sequence(\n [\n { frequency: 880, duration: 0.08 },\n { frequency: 660, duration: 0.08 },\n { frequency: 880, duration: 0.08 },\n { frequency: 660, duration: 0.08 },\n ],\n { type: 'square', gap: 0.04, volume: 0.4, ...options },\n );\n}\n\n/**\n * Plays a gentle descending two-note tone for cancelled operations.\n *\n * @param options - Per-call sound options.\n */\nexport function cancel(options: SoundOptions = {}): void {\n sequence(\n [\n { frequency: 600, duration: 0.09 },\n { frequency: 450, duration: 0.14 },\n ],\n { type: 'triangle', gap: 0.01, volume: 0.65, ...options },\n );\n}\n\n/**\n * Plays a two-note chime for notifications.\n *\n * @param options - Per-call sound options.\n */\nexport function notification(options: SoundOptions = {}): void {\n sequence(\n [\n { frequency: 880, duration: 0.1 },\n { frequency: 1320, duration: 0.18 },\n ],\n { type: 'sine', gap: 0.08, volume: 0.65, ...options },\n );\n}\n\n/**\n * Plays a rising two-note blip for copy operations.\n *\n * @param options - Per-call sound options.\n */\nexport function copy(options: SoundOptions = {}): void {\n sequence(\n [\n { frequency: 900, duration: 0.05 },\n { frequency: 1350, duration: 0.07 },\n ],\n { type: 'sine', gap: 0.03, volume: 0.6, ...options },\n );\n}\n\n/**\n * Plays a soft falling tone for paste operations.\n *\n * @param options - Per-call sound options.\n */\nexport function paste(options: SoundOptions = {}): void {\n tone({\n frequency: 1100,\n endFrequency: 800,\n duration: 0.09,\n type: 'sine',\n volume: 0.65,\n ...options,\n });\n}\n\n/**\n * Plays a descending blip for destructive remove actions.\n *\n * @param options - Per-call sound options.\n */\nexport function remove(options: SoundOptions = {}): void {\n tone({\n frequency: 500,\n endFrequency: 250,\n duration: 0.12,\n type: 'triangle',\n volume: 0.7,\n ...options,\n });\n}\n\n/**\n * Plays a playful rising bloop for reactions.\n *\n * @param options - Per-call sound options.\n */\nexport function reaction(options: SoundOptions = {}): void {\n tone({\n frequency: 500,\n endFrequency: 1400,\n duration: 0.12,\n type: 'sine',\n volume: 0.65,\n ...options,\n });\n}\n"],"mappings":"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"}