node-playsound
v0.3.0
Published
Play WAV, MP3, and FLAC files from Node.js on Windows, macOS, and Linux. Concurrent playback, seeking, and volume control with zero runtime dependencies.
Maintainers
Readme
node-playsound
Play local audio from Node.js with one import. TypeScript, ESM, and zero runtime npm dependencies. WAV, MP3, and FLAC decoding is bundled for Windows, macOS, and Linux—no external player, install-time compiler, or extra download.
Play your first sound
Requires Node.js 22 or newer and a working audio output.
npm install node-playsoundSave this as play.mjs, put sound.mp3 in your current directory, and run
node play.mjs:
import { play } from 'node-playsound';
await play('./sound.mp3').finished;play() returns a handle immediately; file validation and playback start
asynchronously. Observe finished on every playback. It resolves to
'ended' or 'stopped', or rejects with an AudioError if playback fails.
Control a playback
import { play } from 'node-playsound';
const playback = play('./music.flac', { volume: 0.5 });
playback.volume = 0.2;
playback.seek(80); // Absolute seconds from the beginning.
await playback.finished;Call await playback.stop() to stop early and wait for cleanup. A playback is
one run of a file; seeking and volume affect only that run. Seeking is
asynchronous, and seeking at or past the known duration ends playback.
See the complete seeking contract.
Pause an individual playback with playback.pause() and continue with
playback.resume(). Seeking while paused keeps it paused. To inspect progress,
await playback.getTiming() returns { position, duration } in seconds, or
null if playback has settled. Duration can also be null when unknown.
Position means PCM consumed by the mixer, not what is currently audible.
Pause and timing contracts explain acknowledgment,
query failures, and resource ownership.
Play a sound more than once
import { sound } from 'node-playsound';
const notification = sound('./notification.wav', { volume: 0.3 });
await Promise.all([
notification.play().finished,
notification.play({ volume: 0.5 }).finished,
]);The plays overlap and finish independently. A Sound retains a path and
settings; it does not preload the file. Each call reopens it and creates a
new playback. The audio engine is reused between nearby plays.
Own playback in an application
Use a Player when a service, window, worker, or test needs to stop all of
its audio together:
import { Player } from 'node-playsound';
const audio = new Player();
try {
await audio.play(new URL('./sound.mp3', import.meta.url)).finished;
} finally {
await audio.close();
}close() stops the player's pending and active sounds, waits for cleanup,
and permanently closes that player. Other players are independent.
For system audio controls, new Player({ applicationName: 'My app' }) supplies
an application label; the default is your entry-point filename. Windows WASAPI
and Linux PulseAudio/PipeWire use it. macOS still identifies the isolated helper
process. Platform details explain the distinction.
Pass an AbortSignal to play() to tie a sound to an operation's cancellation.
Recipes cover cancellation, background notifications, paths, and shutdown.
Support
| System | Architectures | Audio output | | --- | --- | --- | | Windows 10/11 | x64; ARM64 on Windows 11 | System audio, normally WASAPI | | macOS 13+ | Intel and Apple Silicon | Core Audio | | Linux, glibc 2.35+ | x64 and ARM64 | PulseAudio/PipeWire compatibility or ALSA |
Only regular local files and file: URL objects are accepted. HTTP streams,
AAC/M4A, Ogg, browsers, Alpine/musl, and 32-bit systems are not supported.
Linux needs its system audio libraries and an accessible audio session;
headless containers often have neither. Troubleshooting
covers missing devices, paths, codecs, and bundled deployments.
Playback streams through bounded buffers. There is no unbounded decoded cache or permanent background process. The default limit is 64 pending/active plays per player; reaching it fails the new play rather than queuing it.
Volume is linear gain from 0 to 1; overlapping loud recordings can clip.
Completion means the engine consumed the audio, so a little audio may remain
in device buffers. Gapless transitions,
and sample-accurate scheduling are not provided.
Documentation
- API reference: every option, lifecycle rule, and error code.
- Recipes: common application patterns and runnable examples.
- Troubleshooting: diagnose a failed or silent playback.
- Design: resource ownership, streaming, and process isolation.
- Development · Publishing · Changelog.
- Validation evidence: automated coverage and outstanding hardware checks.
CC0-1.0. Bundled miniaudio uses its public-domain option; see third-party notices.
