Pixels and Waveforms: Creative Coding in the Browser.
Two browser APIs turn a plain web page into a creative instrument: Canvas 2D for pixels and the Web Audio API for sound. Together they cover generative art, data visualization, music tools, games, and accessible audio feedback. This guide is hands-on — every snippet runs if you paste it into NitroIDE's preview.
Canvas 2D: the immediate-mode drawing board
Canvas is immediate-mode: you issue draw commands, pixels appear. No scene graph, no retained objects — which makes it blazing fast for custom rendering:
function frame(t) {
ctx.fillStyle = '#0a0a0a';
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < 120; i++) {
const x = canvas.width / 2 + Math.cos(t / 500 + i) * i * 2;
const y = canvas.height / 2 + Math.sin(t / 500 + i) * i * 2;
ctx.fillStyle = `hsl(${(t / 20 + i * 3) % 360}, 80%, 60%)`;
ctx.fillRect(x, y, 4, 4);
}
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
Crisp on every screen: canvas looks blurry on high-DPI displays unless you scale it. Multiply canvas.width/height by devicePixelRatio and call ctx.scale(devicePixelRatio, devicePixelRatio) — one of the most common canvas bugs, fixed in two lines.
Crisp text on canvas
Canvas text rendering is notoriously rough compared to DOM text. Practical fixes: always set an explicit ctx.font (don't rely on the 10px default), enable ctx.textBaseline deliberately, and for editor-grade typography consider shaping text properly — libraries like HarfBuzz compiled to WASM exist precisely because complex scripts need real text shaping, which canvas won't do for you.
Web Audio: a modular synthesizer in JS
The Web Audio API is a graph of nodes: sources → effects → destination. Building a playable synth takes surprisingly little code:
button.addEventListener('click', () => {
const ctx = new AudioContext();
const osc = ctx.createOscillator();
const gain = ctx.createGain();
const filter = ctx.createBiquadFilter();
osc.type = 'sawtooth'; osc.frequency.value = 220;
filter.type = 'lowpass'; filter.frequency.value = 1200;
gain.gain.setValueAtTime(0, ctx.currentTime);
gain.gain.linearRampToValueAtTime(0.4, ctx.currentTime + 0.05);
gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 1.2);
osc.connect(filter).connect(gain).connect(ctx.destination);
osc.start(); osc.stop(ctx.currentTime + 1.3);
});
That envelope — quick attack, exponential decay — is what separates "beep" from "instrument." Everything is scheduled on the audio clock (ctx.currentTime), which is sample-accurate and independent of frame rate.
Sample-accurate timing with AudioWorklet
For custom DSP — a distortion effect, a granular sampler, a metronome that never drifts — you need code running on the audio thread itself. AudioWorklet replaced the old (now removed) ScriptProcessorNode:
class Metronome extends AudioWorkletProcessor {
process(inputs, outputs) {
const out = outputs[0][0];
for (let i = 0; i < out.length; i++) {
// emit a click every 0.5s, sample-accurate
out[i] = (this.pos++ % 24000 < 200) ? 0.8 : 0;
}
return true; // keep alive
}
}
registerProcessor('metronome', Metronome);
new AudioWorkletNode(ctx, 'metronome').connect(ctx.destination);
Because this runs on the audio rendering thread, timing is sample-accurate — no setInterval drift. Keep the process() method lean: no allocations, no async work, or you'll hear glitches.
UI sonification: sound as accessibility
Sound isn't just for music apps. Short, subtle audio cues — a soft tick on toggle, a rising pitch as a progress bar fills — give non-visual feedback that helps everyone, including screen-reader users navigating custom controls. The Web Audio snippets above are all you need; keep cues under 200 ms and always respect prefers-reduced-motion-style user preferences for sound where your app offers a mute.
The one rule of web audio: AudioContext starts suspended until a user gesture. Always create or resume it inside a click/keydown handler — otherwise you'll debug silence for an hour. This is a browser autoplay policy, not a bug in your code.
Make Some Noise. Draw Some Pixels.
Creative coding needs instant feedback — get it in a zero-setup browser IDE.
Launch NitroIDE