The Web Audio API is more powerful than most developers realize — it's capable of running full synth engines in the browser. This article shows how to build a basic polyphonic synthesizer from scratch using raw Web Audio primitives. Great for interactive music apps, education tools, or browser-based DAWs.
What We’ll Build
- Multiple simultaneous notes (polyphony)
- ADSR envelope for smooth attack and release
- Keyboard input for triggering sounds
Step 1: Create the Audio Context
Set up your Web Audio graph entry point:
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
Step 2: Define a Note Class
This encapsulates oscillator, gain (for volume), and envelope behavior:
class SynthNote {
constructor(freq) {
this.osc = audioCtx.createOscillator();
this.gain = audioCtx.createGain();
this.osc.type = 'sawtooth';
this.osc.frequency.value = freq;
this.osc.connect(this.gain);
this.gain.connect(audioCtx.destination);
const now = audioCtx.currentTime;
this.gain.gain.setValueAtTime(0, now);
this.gain.gain.linearRampToValueAtTime(0.5, now + 0.01); // attack
this.osc.start(now);
}
stop() {
const now = audioCtx.currentTime;
this.gain.gain.linearRampToValueAtTime(0, now + 0.3); // release
this.osc.stop(now + 0.3);
}
}
Step 3: Add Keyboard Controls
Trigger notes with key events and a simple mapping:
const activeNotes = {};
const keyMap = {
'a': 261.63, // C4
's': 293.66, // D4
'd': 329.63, // E4
'f': 349.23, // F4
'g': 392.00, // G4
};
document.addEventListener('keydown', e => {
const freq = keyMap[e.key];
if (freq && !activeNotes[e.key]) {
activeNotes[e.key] = new SynthNote(freq);
}
});
document.addEventListener('keyup', e => {
if (activeNotes[e.key]) {
activeNotes[e.key].stop();
delete activeNotes[e.key];
}
});
Optional: Enhance with Filters or LFO
You can easily insert filters between oscillator and destination:
const filter = audioCtx.createBiquadFilter();
filter.type = 'lowpass';
filter.frequency.value = 1000;
this.osc.connect(filter);
filter.connect(this.gain);
Pros and Cons
✅ Pros
- Totally self-contained and fast to load
- Works offline — no libraries or backend required
- Foundation for more advanced synth engines
⚠️ Cons
- No MIDI support out of the box (but possible with Web MIDI API)
- Timing can drift slightly without clock sync
- Requires handling audio unlock on mobile devices
🚀 Alternatives
- Tone.js: Higher-level abstractions for synths, patterns, and effects
- WAMs: Web Audio Modules for plugin-style architectures
- WebAssembly DSP: For ultra-performant audio engines
Summary
This setup is a powerful base for browser-based synths and interactive audio. By keeping things native, you get instant load times and full control. Extend this with modulation, filters, or sequencing and you’ve got a full playground for web music.
If this was useful, you can support me here: buymeacoffee.com/hexshift