zsk-use-sound
v1.0.1
Published
A TypeScript-first React hook for sound effects, inspired by the excellent `use-sound` package and extended with a browser-generated fallback mode.
Maintainers
Readme
zsk-use-sound
A TypeScript-first React hook for sound effects, inspired by the excellent use-sound package and extended with a browser-generated fallback mode.
Why this package
This hook gives you one API for two sound strategies:
- File mode: Use audio files (mp3, wav, etc.) powered by Howler.
- Generated mode: If no file is provided, it generates a click-like sound in the browser using the Web Audio API.
This is useful when you want sound feedback but do not always want to bundle and manage sound assets.
Installation
bun add zsk-use-soundor
npm install zsk-use-soundPeer dependency:
- React 18+
Quick Start
import { useSound } from 'zsk-use-sound'
export function SaveButton() {
const [play] = useSound({
file: '/sounds/click.mp3',
volume: 0.5,
})
return <button onClick={() => play()}>Save</button>
}API
useSound(options?)
Returns a tuple:
- play: function to trigger sound playback
- exposedData: helper controls and runtime metadata
const [play, exposedData] = useSound(options)Options
type BuiltInSoundType = 'click' | 'soft-click' | 'tick'
interface UseSoundOptions {
file?: string | string[]
type?: BuiltInSoundType
id?: string
volume?: number
playbackRate?: number
interrupt?: boolean
soundEnabled?: boolean
sprite?: Record<string, [number, number]>
onload?: (this: Howl) => void
// Plus delegated Howler options
}Behavior:
- If file is passed: hook runs in file mode using Howler.
- If file is omitted: hook runs in generated mode and uses built-in Web Audio click profiles.
- Default type is click.
play(options?)
interface PlayOptions {
id?: string
forceSoundEnabled?: boolean
playbackRate?: number
}Exposed data
interface ExposedData {
sound: Howl | null
stop: (id?: number) => void
pause: (id?: number) => void
duration: number | null
mode: 'file' | 'generated'
}Usage Patterns
1) File mode (use-sound style)
import { useSound } from 'zsk-use-sound'
export function NotificationButton() {
const [play, { stop, mode }] = useSound({
file: ['/sounds/ping.wav', '/sounds/ping.mp3'],
volume: 0.4,
interrupt: true,
})
return (
<>
<button onClick={() => play()}>Play</button>
<button onClick={() => stop()}>Stop</button>
<small>mode: {mode}</small>
</>
)
}2) Generated mode (no file)
import { useSound } from 'zsk-use-sound'
export function UIInteractions() {
const [play, { mode }] = useSound({
type: 'soft-click',
volume: 0.6,
})
return <button onClick={() => play()}>Click me ({mode})</button>
}3) Global sound toggle
import { useSound } from 'zsk-use-sound'
export function MutedAwareAction({ isEnabled }: { isEnabled: boolean }) {
const [play] = useSound({
file: '/sounds/action.mp3',
soundEnabled: isEnabled,
})
return <button onClick={() => play()}>Action</button>
}Why Howler
Howler is used for file playback mode because it offers:
- Cross-browser audio consistency: normalizes browser differences.
- Flexible source handling: supports source arrays and format fallback.
- Sprite support: multiple sounds in one file with IDs.
- Stable playback controls: volume, rate, stop, pause, and event hooks.
- Mature ecosystem: widely used and battle-tested.
In short, Howler handles the hard edges of browser audio so your hook API stays simple.
How this package is better for this use case
Compared to a direct baseline implementation, this package adds:
- Dual-mode behavior: file-based audio plus generated fallback in one hook.
- No-any TypeScript API: explicit options and return types.
- Better type surface for consumers: exported public types directly from the package.
- Built-in sound presets: click, soft-click, tick.
- Runtime mode visibility: exposedData.mode tells you how playback is happening.
Credit
This package is inspired by:
- Josh Comeau's original use-sound project: https://github.com/joshwcomeau/use-sound
Many API ideas (tuple return shape, play options, exposed control object, and Howler-based file mode approach) are adapted from that excellent work.
Thank you to Josh Comeau for creating and sharing the original library.
TypeScript Exports
You can import runtime and type APIs directly:
import { useSound } from 'zsk-use-sound'
import type {
BuiltInSoundType,
UseSoundOptions,
PlayOptions,
ExposedData,
ReturnedValue,
} from 'zsk-use-sound'Notes
- Generated mode relies on Web Audio API availability in the browser.
- Browsers often require user interaction before sound can play.
- For SSR environments, playback happens client-side only.
License
ISC
