How to Build a Zero-Dependency Audio Synth in the Browser Using Web Audio API

The Web Audio API is a criminally underused tool for generating dynamic, real-time sound. This post walks through creating a mini synth from scratch — no libraries, no UI frameworks, just pure browser-native sound generation using JavaScript. Ideal for game devs, creative coders, or anyone who wants to learn procedural audio at a low level. Why Use Web Audio API Directly? No external libraries or builds needed Precise control over oscillators, filters, and timing Useful in games, interactive apps, or generative art Step 1: Create the Audio Context and Oscillator The AudioContext is your entry point to the Web Audio graph: const ctx = new (window.AudioContext || window.webkitAudioContext)(); const osc = ctx.createOscillator(); osc.type = 'sawtooth'; // Try sine, square, triangle, etc. osc.frequency.value = 440; // A4 osc.start(); Step 2: Add Envelope Control (ADSR) We'll use a GainNode to fade the volume in and out for each note: const gain = ctx.createGain(); gain.gain.setValueAtTime(0, ctx.currentTime); gain.gain.linearRampToValueAtTime(1, ctx.currentTime + 0.1); // attack gain.gain.linearRampToValueAtTime(0, ctx.currentTime + 0.6); // decay + release osc.connect(gain).connect(ctx.destination); Step 3: Wire It to a Key Press Basic synth: play a tone on key press, release it after a fixed time: document.addEventListener('keydown', (e) => { const note = e.key.toUpperCase(); const freqMap = { A: 440, W: 466, S: 494, D: 523 }; // add more if (!freqMap[note]) return; const o = ctx.createOscillator(); const g = ctx.createGain(); o.type = 'square'; o.frequency.value = freqMap[note]; g.gain.setValueAtTime(0, ctx.currentTime); g.gain.linearRampToValueAtTime(0.9, ctx.currentTime + 0.05); g.gain.linearRampToValueAtTime(0, ctx.currentTime + 0.4); o.connect(g).connect(ctx.destination); o.start(); o.stop(ctx.currentTime + 0.5); }); Step 4: Add Filter for Character A low-pass filter can shape the tone: const filter = ctx.createBiquadFilter(); filter.type = 'lowpass'; filter.frequency.value = 800; osc.connect(filter).connect(gain).connect(ctx.destination); Pros and Cons ✅ Pros Completely free, native to every modern browser Fine-grained control of sound generation Great for games, audio toys, or learning synthesis ⚠️ Cons No polyphony unless manually managed Inconsistent latency across devices Touch event restrictions on mobile (needs user interaction)

Apr 23, 2025 - 02:15
 0
How to Build a Zero-Dependency Audio Synth in the Browser Using Web Audio API

The Web Audio API is a criminally underused tool for generating dynamic, real-time sound. This post walks through creating a mini synth from scratch — no libraries, no UI frameworks, just pure browser-native sound generation using JavaScript. Ideal for game devs, creative coders, or anyone who wants to learn procedural audio at a low level.

Why Use Web Audio API Directly?


  • No external libraries or builds needed
  • Precise control over oscillators, filters, and timing
  • Useful in games, interactive apps, or generative art

Step 1: Create the Audio Context and Oscillator


The AudioContext is your entry point to the Web Audio graph:

const ctx = new (window.AudioContext || window.webkitAudioContext)();
const osc = ctx.createOscillator();
osc.type = 'sawtooth'; // Try sine, square, triangle, etc.
osc.frequency.value = 440; // A4
osc.start();

Step 2: Add Envelope Control (ADSR)


We'll use a GainNode to fade the volume in and out for each note:

const gain = ctx.createGain();
gain.gain.setValueAtTime(0, ctx.currentTime);
gain.gain.linearRampToValueAtTime(1, ctx.currentTime + 0.1); // attack
gain.gain.linearRampToValueAtTime(0, ctx.currentTime + 0.6); // decay + release
osc.connect(gain).connect(ctx.destination);

Step 3: Wire It to a Key Press


Basic synth: play a tone on key press, release it after a fixed time:

document.addEventListener('keydown', (e) => {
const note = e.key.toUpperCase();
const freqMap = { A: 440, W: 466, S: 494, D: 523 }; // add more

if (!freqMap[note]) return;

const o = ctx.createOscillator();
const g = ctx.createGain();
o.type = 'square';
o.frequency.value = freqMap[note];
g.gain.setValueAtTime(0, ctx.currentTime);
g.gain.linearRampToValueAtTime(0.9, ctx.currentTime + 0.05);
g.gain.linearRampToValueAtTime(0, ctx.currentTime + 0.4);

o.connect(g).connect(ctx.destination);
o.start();
o.stop(ctx.currentTime + 0.5);
});

Step 4: Add Filter for Character


A low-pass filter can shape the tone:

const filter = ctx.createBiquadFilter();
filter.type = 'lowpass';
filter.frequency.value = 800;
osc.connect(filter).connect(gain).connect(ctx.destination);

Pros and Cons


✅ Pros


  • Completely free, native to every modern browser
  • Fine-grained control of sound generation
  • Great for games, audio toys, or learning synthesis

⚠️ Cons


  • No polyphony unless manually managed
  • Inconsistent latency across devices
  • Touch event restrictions on mobile (needs user interaction)