webaudio-node
Version:
Full-featured Web Audio API for Node.js with WASM and SIMD optimizations
556 lines (401 loc) โข 15.9 kB
Markdown
# Web Audio Node
[](https://github.com/monteslu/webaudio-node/actions/workflows/ci.yml)
[](https://codecov.io/gh/monteslu/webaudio-node)
[](https://www.npmjs.com/package/webaudio-node)
[](https://opensource.org/licenses/ISC)
[](https://nodejs.org)
**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](./docs/WASM_AUDIO_DECODERS.md)
- **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
```bash
npm install webaudio-node
```
### Requirements
- Node.js 20+
- No build step required - uses pre-compiled WASM
## ๐ต CLI Usage
Play audio files directly from the command line:
```bash
# Play any supported audio file
npx webaudio-node music.mp3
# Or after global install
npm install -g webaudio-node
webaudio-node sound.wav
```
**Supported formats:** MP3, WAV, FLAC, OGG, AAC
### ๐ค Microphone Effects
Transform your voice in real-time with built-in effects:
```bash
# Add reverb to your microphone
webaudio-node --reverb
# Auto-tune your voice (vocoder-style effect)
webaudio-node --auto-tune
# Show all options
webaudio-node --help
```
Press Ctrl+C to stop the effect and close the microphone.
## ๐ Quick Start
### Simple Beep
```javascript
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
```javascript
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!)
```javascript
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.
```javascript
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 cleanup
```
**Note:** 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.
```javascript
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 waveforms
- `createBufferSource()` - Play audio buffers
- `createGain()` - 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:
```javascript
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
```javascript
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](./docs/threading.md).
## ๐ Examples
### Layered Music Playback
```javascript
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
```javascript
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
```javascript
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](./docs/automated_benchmarks.md) 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_sample` counters
- 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](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:
```bash
# 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:wasm
```
### Available Scripts
```bash
# 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:
```bash
npm test
```
Tests 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](./docs/automated_benchmarks.md))
- **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](docs/threading.md) for Worker Thread implementation plan.
## ๐ License
MIT License - see [LICENSE](LICENSE)
## ๐ Credits
- SDL2 for audio output
- [Emscripten](https://emscripten.org/) for WASM compilation
- [@kmamal/sdl](https://github.com/kmamal/node-sdl) for SDL bindings
- [dr_libs](https://github.com/mackron/dr_libs) for WASM audio decoding (MP3, WAV, FLAC)
- [stb_vorbis](https://github.com/nothings/stb) for OGG Vorbis decoding
- [fdk-aac](https://github.com/mstorsjo/fdk-aac) for AAC decoding
- [node-web-audio-api](https://github.com/ircam-ismm/node-web-audio-api) for Rust implementation reference
---
**Made with โค๏ธ for game developers, music producers, and audio enthusiasts**
๐ฎ ๐ต ๐