Web Audio API · Synthesis · Algorithmic Composition
Procedural Music with Web Audio API
The Web Audio API is a full synthesis engine — oscillators, filters,
envelopes, convolution reverb, and precise timing. This tutorial builds
an algorithmic music generator: a tempo clock, a scale-aware sequencer,
a simple synthesizer patch with ADSR, and a chord progression engine.
1Pitch and scale utilities
// MIDI note → frequency in Hz const mtof = note => 440 * Math.pow(2,
(note - 69) / 12); // Common scales as semitone intervals from root
const SCALES = { major: [0, 2, 4, 5, 7, 9, 11], minor: [0, 2, 3, 5, 7,
8, 10], pentatonic: [0, 2, 4, 7, 9], blues: [0, 3, 5, 6, 7, 10],
dorian: [0, 2, 3, 5, 7, 9, 10], }; // Build all notes of a scale
across N octaves function buildScale(root = 60, scaleName = 'minor',
octaves = 3) { const intervals = SCALES[scaleName]; const notes = [];
for (let oct = 0; oct < octaves; oct++) { for (const interval of
intervals) { notes.push(root + oct * 12 + interval); } } return notes;
} const palette = buildScale(48, 'pentatonic', 3); // → [48, 50, 52,
55, 57, 60, 62, 64, 67, 69, 72, 74, 76, 79, 81]
The Web Audio API has sample-accurate timing, but
setInterval is jittery. Solve this with a
lookahead scheduler: fire a JS callback every 25 ms,
but schedule notes 100 ms into the future:
const BPM = 120; const BEAT = 60 / BPM; // seconds per beat const LOOK
= 0.1; // lookahead window (s) const TICK = 25; // scheduler interval
(ms) let nextNoteTime = 0; let currentBeat = 0; let timerID = null;
let playing = false; let onBeat = null; // callback: (beat, time) =>
void function scheduler() { while (nextNoteTime < audio.currentTime
+ LOOK) { if (onBeat) onBeat(currentBeat, nextNoteTime); nextNoteTime
+= BEAT; currentBeat = (currentBeat + 1) % 32; } timerID =
setTimeout(scheduler, TICK); } function startClock() { if (playing)
return; audio.resume(); nextNoteTime = audio.currentTime + 0.05;
currentBeat = 0; playing = true; scheduler(); } function stopClock() {
clearTimeout(timerID); playing = false; }
This pattern is from Chris Wilson's canonical Web Audio clock article.
The scheduler never drifts because you are always scheduling
relative toaudio.currentTime, not wall time.
4Scale-aware melody sequencer
const notes = buildScale(48, 'pentatonic', 2); // Random walk: move
±1-2 steps in scale with wrap let pos = Math.floor(notes.length / 2);
function nextMelodyNote() { const step = Math.random() < 0.7 ?
(Math.random() < 0.5 ? 1 : -1) // small step most often :
(Math.random() < 0.5 ? 2 : -2); // occasional leap pos =
Math.max(0, Math.min(notes.length - 1, pos + step)); return
notes[pos]; } // Probability of rests per beat const REST_PROB = 0.15;
onBeat = (beat, time) => { if (Math.random() < REST_PROB) return;
const midi = nextMelodyNote(); const freq = mtof(midi); const dur =
BEAT * (Math.random() < 0.3 ? 2 : 1); // some notes held
playNote(freq, time, dur * 0.9, { attack: 0.01, decay: 0.08, sustain:
0.5, release: 0.15 }); };
5Chord progressions
// I-VI-IV-V in C minor (MIDI root 60) const PROGRESSIONS = {
'i-VI-III-VII': [[0,3,7], [8,11,3], [3,7,10], [10,1,5]], 'i-iv-v-i':
[[0,3,7], [5,8,0], [7,11,2], [0,3,7]], }; const PROG =
PROGRESSIONS['i-VI-III-VII']; let chordIndex = 0; function
playChord(root, intervals, time) { for (const interval of intervals) {
const freq = mtof(root + interval); playNote(freq, time, BEAT * 4, {
attack: 0.08, decay: 0.2, sustain: 0.4, release: 0.8 }); } } // Change
chord every 4 beats onBeat = (beat, time) => { if (beat % 4 === 0) {
playChord(48, PROG[chordIndex % PROG.length], time); chordIndex++; }
// Melody runs on top if (Math.random() > 0.15) {
playNote(mtof(nextMelodyNote()), time, BEAT * 0.85); } };
The impulse-response reverb (convolverNode) created from synthetic
noise sounds surprisingly natural. For real spaces, load a recorded IR
file using fetch + decodeAudioData.
Frequently Asked Questions
What will I learn in this tutorial?
Generate algorithmic music in the browser using Web Audio API oscillators, sequencers, chord progressions, and basic synthesis patches.
What topics are covered in this tutorial?
This tutorial covers: Pitch and scale utilities, ADSR synthesizer patch, Precise clock with lookahead scheduling, Scale-aware melody sequencer, Chord progressions, Reverb and effects.
What tools and technologies does this tutorial use?
This tutorial uses Web Audio API, Synthesis, Algorithmic Composition.
How long does this tutorial take?
This tutorial takes approximately 55 minutes to complete.
What prerequisites do I need before starting?
This is a Intermediate-level tutorial — no special preparation beyond basic JavaScript is assumed.