webaudio-node
v0.6.1
Published
Full-featured Web Audio API for Node.js with WASM and SIMD optimizations
Downloads
35
Maintainers
Readme
Web Audio Node
Web Audio API for Node.js with WASM and SIMD optimizations
High-performance, browser-compatible Web Audio API for Node.js. Perfect for audio processing, sound generation, game audio, and music production.
⭐ Features
✨ Web Audio API Support
- Core audio nodes (Oscillator, Gain, BiquadFilter, BufferSource)
- AudioParam automation with scheduling
- OfflineAudioContext for fast non-realtime rendering
- AudioContext for real-time playback
- 5 audio formats via WASM decoders (MP3, WAV, FLAC, OGG, AAC) - see WASM Audio Decoders
- High-quality resampling - Speex resampler with SIMD (same as Firefox uses)
- Automatic sample rate detection - matches system audio device
🚀 High Performance
- WASM with SIMD optimizations - beats Rust implementation in 90% of benchmarks (55/61)
- ~2,600x faster than realtime offline rendering
- Fast resampling - Speex quality level 1 (200-850x realtime for common sample rates)
- Sample-accurate timing - all audio timing managed in WASM using sample counters
- Zero JavaScript overhead in audio rendering - JS only handles I/O through SDL
🎮 Perfect for Games
- Procedural sound effect generation
- Mix multiple audio sources
- Low-latency playback with SDL2
📦 Installation
npm install webaudio-nodeRequirements
- Node.js 20+
- No build step required - uses pre-compiled WASM
🎵 CLI Usage
Play audio files directly from the command line:
# Play any supported audio file
npx webaudio-node music.mp3
# Or after global install
npm install -g webaudio-node
webaudio-node sound.wavSupported formats: MP3, WAV, FLAC, OGG, AAC
🎤 Microphone Effects
Transform your voice in real-time with built-in effects:
# Add reverb to your microphone
webaudio-node --reverb
# Auto-tune your voice (vocoder-style effect)
webaudio-node --auto-tune
# Show all options
webaudio-node --helpPress Ctrl+C to stop the effect and close the microphone.
🚀 Quick Start
Simple Beep
import { AudioContext } from 'webaudio-node';
const ctx = new AudioContext();
// Create 440Hz oscillator
const osc = ctx.createOscillator();
osc.frequency.value = 440;
osc.connect(ctx.destination);
// Play for 1 second
await ctx.resume();
osc.start();
setTimeout(() => {
osc.stop();
ctx.close();
}, 1000);Play MP3 File
import { AudioContext } from 'webaudio-node';
import { readFileSync } from 'fs';
const ctx = new AudioContext({ sampleRate: 48000 });
const audioData = readFileSync('./music.mp3');
// Decode MP3
const buffer = await ctx.decodeAudioData(audioData.buffer);
// Play
const source = ctx.createBufferSource();
source.buffer = buffer;
source.connect(ctx.destination);
source.start();
await ctx.resume();Procedural Sound Effect (Ultra-Fast!)
import { OfflineAudioContext } from 'webaudio-node';
// Generate laser sound offline
const offlineCtx = new OfflineAudioContext({
numberOfChannels: 2,
length: 14400,
sampleRate: 48000
});
const osc = offlineCtx.createOscillator();
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(800, 0);
osc.frequency.exponentialRampToValueAtTime(200, 0.3);
const gain = offlineCtx.createGain();
gain.gain.setValueAtTime(0.5, 0);
gain.gain.exponentialRampToValueAtTime(0.01, 0.3);
osc.connect(gain);
gain.connect(offlineCtx.destination);
osc.start(0);
// Renders in ~1ms (14,400x faster than realtime!)
const laserBuffer = await offlineCtx.startRendering();📚 API Reference
AudioContext
Real-time audio playback with chunk-ahead buffering.
const ctx = new AudioContext({
sampleRate: 48000, // Default: 44100
numberOfChannels: 2 // Default: 2 (stereo)
});
await ctx.resume(); // Start audio
await ctx.suspend(); // Pause audio
await ctx.close(); // Stop and cleanupNote: Real-time AudioContext uses chunk-ahead buffering (1-2 second latency), which is perfect for background music and non-interactive audio.
OfflineAudioContext
Non-realtime rendering for ultra-fast audio processing.
const offlineCtx = new OfflineAudioContext({
numberOfChannels: 2,
length: 48000, // samples
sampleRate: 48000
});
const buffer = await offlineCtx.startRendering();Performance: Renders ~50,000x faster than realtime.
Audio Nodes
Source Nodes:
createOscillator()- Sine, square, sawtooth, triangle waveformscreateBufferSource()- Play audio bufferscreateGain()- Volume control
Processing:
createBiquadFilter()- Lowpass, highpass, bandpass, notch filters
Supported Node Types
16/17 applicable Web Audio API nodes implemented (94% coverage)
| Node | AudioContext | OfflineAudioContext | | ---------------------- | ------------ | ------------------- | | AudioDestinationNode | ✅ | ✅ | | AudioBufferSourceNode | ✅ | ✅ | | OscillatorNode | ✅ | ✅ | | ConstantSourceNode | ✅ | ✅ | | GainNode | ✅ | ✅ | | BiquadFilterNode | ✅ | ✅ | | IIRFilterNode | ✅ | ✅ | | DelayNode | ✅ | ✅ | | WaveShaperNode | ✅ | ✅ | | ConvolverNode | ✅ | ✅ | | DynamicsCompressorNode | ✅ | ✅ | | PannerNode | ✅ | ✅ | | StereoPannerNode | ✅ | ✅ | | ChannelSplitterNode | ✅ | ✅ | | ChannelMergerNode | ✅ | ✅ | | AnalyserNode | ✅ | ✅ |
Not applicable to Node.js: MediaElement/MediaStream nodes (browser-only), ScriptProcessorNode (deprecated)
AudioParam Automation
All AudioParams support automation:
const osc = ctx.createOscillator();
// Instant change
osc.frequency.value = 880;
// Scheduled changes
osc.frequency.setValueAtTime(440, 0);
osc.frequency.linearRampToValueAtTime(880, 1.0);
osc.frequency.exponentialRampToValueAtTime(220, 2.0);Decoding Audio Files
import { readFileSync } from 'fs';
const ctx = new AudioContext();
const audioData = readFileSync('./audio.mp3');
// Decode MP3/WAV/OGG
const buffer = await ctx.decodeAudioData(audioData.buffer);
console.log(buffer.duration); // seconds
console.log(buffer.numberOfChannels); // 1 or 2
console.log(buffer.sampleRate); // Hz
// Get audio data
const channelData = buffer.getChannelData(0);Note: Audio decoding is currently synchronous (blocks the event loop) but very fast with WASM. Most files decode in milliseconds. For non-blocking decoding in the future, see Threading Plan.
📖 Examples
Layered Music Playback
import { AudioContext } from 'webaudio-node';
import { readFileSync } from 'fs';
const ctx = new AudioContext({ sampleRate: 48000 });
const audioData = readFileSync('./music.mp3');
const buffer = await ctx.decodeAudioData(audioData.buffer);
// Create master gain
const masterGain = ctx.createGain();
masterGain.gain.value = 0.25;
masterGain.connect(ctx.destination);
// Play 5 instances with 1-second delays
for (let i = 0; i < 5; i++) {
const source = ctx.createBufferSource();
source.buffer = buffer;
const instanceGain = ctx.createGain();
instanceGain.gain.value = 1.0;
source.connect(instanceGain);
instanceGain.connect(masterGain);
const startTime = ctx.currentTime + i;
source.start(startTime);
}
await ctx.resume();Filtered Oscillator
import { OfflineAudioContext } from 'webaudio-node';
import { writeFileSync } from 'fs';
const ctx = new OfflineAudioContext({
numberOfChannels: 2,
length: 48000 * 3, // 3 seconds
sampleRate: 48000
});
// Create filtered sawtooth
const osc = ctx.createOscillator();
osc.type = 'sawtooth';
osc.frequency.value = 110; // A2
const filter = ctx.createBiquadFilter();
filter.type = 'lowpass';
filter.frequency.value = 2000;
filter.Q.value = 5;
// Sweep filter
filter.frequency.setValueAtTime(200, 0);
filter.frequency.exponentialRampToValueAtTime(8000, 3);
const gain = ctx.createGain();
gain.gain.value = 0.3;
osc.connect(filter);
filter.connect(gain);
gain.connect(ctx.destination);
osc.start(0);
const rendered = await ctx.startRendering();
// Write to file...Game Sound Effects Generator
import { OfflineAudioContext } from 'webaudio-node';
async function generateLaserSound() {
const ctx = new OfflineAudioContext({
numberOfChannels: 2,
length: 14400,
sampleRate: 48000
});
const osc = ctx.createOscillator();
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(600 + Math.random() * 400, 0);
osc.frequency.exponentialRampToValueAtTime(100 + Math.random() * 200, 0.3);
const gain = ctx.createGain();
gain.gain.setValueAtTime(0.3, 0);
gain.gain.exponentialRampToValueAtTime(0.01, 0.3);
osc.connect(gain);
gain.connect(ctx.destination);
osc.start(0);
return await ctx.startRendering();
}
// Generate 100 variations in ~100ms
const sounds = await Promise.all(
Array(100)
.fill(0)
.map(() => generateLaserSound())
);⚡ Performance
webaudio-node delivers excellent performance for audio workloads. See detailed benchmarks for comprehensive comparisons.
Offline Rendering Speed:
| Duration | Render Time | Speed vs Realtime | | -------- | ----------- | ----------------- | | 1s | ~0.38ms | ~2,600x | | 5s | ~1.9ms | ~2,600x | | 10s | ~3.8ms | ~2,600x |
Why so fast? WASM with SIMD optimizations processes 4 audio samples in parallel.
🏗️ Architecture
WASM Backend
Audio graph rendering uses WebAssembly compiled from optimized C++:
┌─────────────────┐
│ JavaScript API │ (Node creation, connections, I/O via SDL)
└────────┬────────┘
│
┌────────▼────────┐
│ WASM Engine │ (Graph rendering, SIMD mixing, timing)
│ │ • Sample-accurate timing with current_sample counters
│ │ • All scheduling handled in WASM
│ │ • Unified binary: graph + nodes + decoders
└────────┬────────┘
│
┌────────▼────────┐
│ SDL2 Output │ (Audio device via @kmamal/sdl)
└─────────────────┘Key features:
- Single unified WASM binary (
dist/webaudio.wasm) contains all audio processing - All audio timing managed in WASM using sample-accurate
current_samplecounters - JavaScript only handles I/O through SDL - no timing logic in JS
- SIMD optimizations (4-8x parallel processing)
- Proper gain node mixing - all inputs mixed together per Web Audio spec
- Sample-accurate BufferSourceNode scheduling
See docs/architecture.md for architectural details.
🔧 Development
Building from Source
WASM modules are pre-compiled and included in the npm package. To rebuild from source:
# Install Emscripten SDK
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
# Build WASM (includes all audio decoders)
cd webaudio-node
npm run build:wasmAvailable Scripts
# Testing
npm test # Run test suite
npm run test:coverage # Run tests with coverage report
# Linting & Formatting
npm run lint # Check code style
npm run lint:fix # Auto-fix linting issues
npm run format # Format code with Prettier
npm run format:check # Check formatting
# Benchmarks
npm run benchmark # Run performance benchmarks
npm run benchmark:docs # Generate benchmark documentation🧪 Testing
Comprehensive Web Audio API test suite:
npm testTests cover:
- All audio nodes
- AudioParam automation
- Audio graph connections
- Rendering accuracy
- Stereo/mono support
Test results: 61/61 tests passing ✅
📁 Project Structure
webaudio-node/
├── src/
│ ├── wasm/ # C++ source for WASM
│ ├── wasm-integration/ # WASM wrapper classes
│ └── javascript/ # JavaScript nodes & AudioParam
├── test/
│ ├── api-suite.js # Comprehensive API tests
│ └── samples/ # Test audio files
├── docs/
│ └── threading.md # Architecture documentation
└── scripts/
└── build-wasm.mjs # WASM build script🖥️ Platform Support
| Platform | Status | Notes | | -------------- | ------ | ------------------ | | macOS ARM64 | ✅ | SIMD optimizations | | macOS x86-64 | ✅ | SIMD optimizations | | Linux ARM64 | ✅ | SIMD optimizations | | Linux x86-64 | ✅ | SIMD optimizations | | Windows x86-64 | ✅ | Requires SDL2 |
🔄 Comparison with Other Libraries
vs node-web-audio-api (Rust)
node-web-audio-api is an excellent, well-maintained Rust implementation. Both libraries offer high-performance audio processing for Node.js.
Key differences:
- Performance: webaudio-node is faster overall in most workloads (90% win rate - see detailed benchmarks)
- Resampling: Speex quality 1 (fast, good quality) vs advanced resampling
- Node Coverage: 16/17 applicable nodes (94%) implemented
- Installation: Pre-compiled WASM (no build toolchain required)
Both libraries are production-ready - choose based on your specific needs!
vs web-audio-engine
- ✅ Much faster (WASM vs pure JS)
- ✅ Better Web Audio API compliance
- ✅ Real-time playback support
- ✅ Comprehensive node coverage
🛣️ Roadmap
Current: 94% Web Audio API compliance with WASM backend
Future:
- Worker Thread rendering for lower latency
- AudioWorklet support
- Additional WebCodecs integration
See docs/threading.md for Worker Thread implementation plan.
📜 License
MIT License - see LICENSE
🙏 Credits
- SDL2 for audio output
- Emscripten for WASM compilation
- @kmamal/sdl for SDL bindings
- dr_libs for WASM audio decoding (MP3, WAV, FLAC)
- stb_vorbis for OGG Vorbis decoding
- fdk-aac for AAC decoding
- node-web-audio-api for Rust implementation reference
Made with ❤️ for game developers, music producers, and audio enthusiasts
🎮 🎵 🔊
