Live coding is the practice of writing code that makes music in real-time. Tools like TidalCycles and Strudel have made this accessible, but I wanted to build my own—one that runs entirely in the browser with no dependencies, synthesizes all its own sounds, and fits the minimal aesthetic of my other projects.
The Pattern Language
The core of any live coding environment is the pattern language. I took inspiration from Strudel's "mini notation" but simplified it for the browser context.
// Basic sequence - 4 sounds per cycle
"kick snare kick snare"
// Subdivision with brackets
"kick [hihat hihat] snare [hihat hihat]"
// Rests with ~
"kick ~ snare ~"
// Stack layers together
stack(
"kick ~ kick ~",
"~ snare ~ snare",
"[hihat hihat hihat hihat]"
)The parser tokenizes the pattern string, handles bracket grouping for subdivision, and converts each token into a scheduled sound event with a normalized time value (0-1 within the cycle).
Sound Synthesis
Every sound is synthesized from scratch using the Web Audio API. No samples, no external files—just oscillators, noise, and filters.
// Kick drum: pitch-swept sine wave
function playKick(time, pitch = 60, decay = 0.3) {
const osc = ctx.createOscillator()
osc.frequency.setValueAtTime(pitch * 2.5, time)
osc.frequency.exponentialRampToValueAtTime(pitch * 0.5, time + 0.1)
// ... gain envelope
}
// Pad: multiple detuned oscillators
function playPad(time, freq = 220) {
const detunes = [-10, -5, 0, 5, 10]
detunes.forEach(detune => {
const osc = ctx.createOscillator()
osc.type = "sine"
osc.frequency.value = freq
osc.detune.value = detune
// ...
})
}Seven synthesized sounds cover the basics: kick,snare,hihat,clap,bass,lead,pad, andpluck. Each accepts parameters to customize the sound.
Arpeggiation
For melodic content, the arp() function takes a set of notes and cycles through them in various orders—up, down, updown, or random. Classic trance arpeggios are just a function call away.
// Ascending arpeggio
arp("C E G B", "up").pluck(4)
// Bouncing arpeggio with delay
arp("A C E", "updown").lead(4).delay(0.125, 0.3)
// Chord - all notes together
chord("C E G").pad(3).reverb(0.6)Effects Chain
The effects are chained using method syntax. Each effect creates or modifies Web Audio nodes in the signal path.
Reverb uses a ConvolverNode with a synthetically generated impulse response— exponentially decaying noise creates a convincing room sound without loading any files.
Delay uses a DelayNode with feedback loop for classic echo effects.
Sidechain ducking (the .duck() effect) triggers a GainNode that drops when the kick hits and recovers over time. This creates the classic EDM "pumping" effect without needing an actual compressor.
// When kick plays, duck the gain
if (duckGainRef.current) {
duckGainRef.current.gain.setValueAtTime(0.3, time)
duckGainRef.current.gain.linearRampToValueAtTime(1, time + 0.1)
}Sample-Accurate Timing
Like the ACID synth and metronome, timing uses the look-ahead scheduler pattern. Events are scheduled at precise AudioContext.currentTime values, not triggered by JavaScript timers. This keeps everything locked to the BPM even during heavy UI activity.
The Editor
The code editor is a simple textarea with line numbers. Hit Cmd/Ctrl+Enter to evaluate and hear changes immediately. Errors display inline. It's minimal but functional—the focus is on the sound, not the IDE features.
Try It
Live Code Music is live. Load a preset, tweak the patterns, and hear the results instantly. No installation, no plugins—just your browser and some code.