Two episodes back, while we were bolting honest knobs onto our sketches, and then again last time while we were building whole design systems, I kept catching myself saying the same thing: once your art is fast and lives in ranges instead of single frames, it's begging to be driven live, in front of people. That itch has been building for a while, honestly - all the way back when we did live coding music and I played a whole track by editing loops while they ran. Well, allez, today the visuals get the same treatment. Today we stop pressing "save and reload" and start performing our graphics, typing code at them while they're already moving :-).
I want to be honest that this is a bit of a mind-flip before it's a technique. Every sketch we've written so far had a clean life cycle: write the code, run it, watch it, stop it, edit, run it again. Live coding throws that away. The program never stops. You reach into a running thing and rewrite pieces of it while it keeps drawing, and the audience sees the whole messy, alive process - the typos, the recoveries, the moment a single number turns a calm grid into a screaming strobe. It's closer to playing an instrument than to shipping software, and once you feel it, plain old edit-save-reload starts to feel like writing letters instead of talking.
A tiny bit of culture first, because it explains why the tools look the way they do. There's a whole community around this, going back to a manifesto-ish group called TOPLAP (around 2004) whose whole deal was: show us your screens. The code should be visible, projected, part of the show - no hiding behind a laptop pretending you're checking email. Out of that grew the algorave - a night where people dance to music and visuals generated live by code typed on stage, projector blazing, everybody watching the source change. The vibe is playful and a little punk. Nobody's pretending the code is perfect. The mistakes are part of it.
That culture baked one rule deep into every live-coding tool you'll meet: evaluating a block must be instant, and it must never crash the whole show. Hold onto that, because it's the design constraint that shapes everything we build today. A compiler error at your desk is a shrug. A compiler error that blacks out the projector in front of a hundred dancing people is a catastrophe. So the tools are built to fail soft.
So how does it actually work? The magic feels like sorcery until you see it, and then - like the GUI library two episodes ago - it's obvious forever. The whole thing rests on one idea: the draw loop reads from a slot, and you rewrite what's in the slot live. The animation never stops looping over the slot; you just keep swapping its contents.
// the heart of live coding: a "current draw function" living in a slot.
// the loop below NEVER stops - it just calls whatever is in the slot, every frame.
let currentDraw = (ctx, t, size) => {
ctx.fillStyle = "#111";
ctx.fillRect(0, 0, size, size); // a boring black screen to start
};
function loop(t) {
const ctx = canvas.getContext("2d");
currentDraw(ctx, t / 1000, canvas.width); // call WHATEVER is in the slot right now
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
Nothing surprising there yet - it's a standard animation loop, same shape we've used since episode 3. The twist is that currentDraw is a variable, not a fixed function. So if I can somehow rewrite that variable while the loop is running, the very next frame draws with my new code. And the tool for turning a string of code into a live function is JavaScript's much-maligned Function constructor.
// take a STRING of code the performer typed, and turn it into a live draw function.
// this is what "evaluating a block" actually does under the hood.
function evaluate(codeString) {
// build a function of (ctx, t, size) from the text the performer typed
const fn = new Function("ctx", "t", "size", codeString);
currentDraw = fn; // swap it into the slot - next frame uses it!
}
// somewhere a keypress (say Ctrl+Enter) calls:
evaluate(`
ctx.fillStyle = "hsl(" + (t * 60 % 360) + ", 80%, 50%)";
ctx.fillRect(0, 0, size, size);
`);
Read that twice, because that's genuinly the whole engine. The performer types code into a text box, hits a key, we wrap the text in new Function(...), and drop the result into currentDraw. The loop was already running and never noticed - it just found new instructions in the slot on its next tick. That is live coding, stripped to its bones. Everything else - the pretty editors, the fancy visual libraries - is comfort and polish on top of this exact move.
Here's where that TOPLAP rule earns its keep. Right now, if the performer types a typo, new Function throws, and depending on how you wired it, either the swap fails silently or the whole thing dies. Neither is acceptable on stage. What we want: a bad block gets ignored, the last good visual keeps playing, and maybe a little error message flashes so the performer knows to fix it. So wrap the whole thing in a net.
// forgiving evaluation: a broken block NEVER stops the show.
// on error we keep the last-good draw function and just flash a warning.
function evaluateSafe(codeString) {
try {
const fn = new Function("ctx", "t", "size", codeString);
fn(document.createElement("canvas").getContext("2d"), 0, 100); // dry-run once to catch throws
currentDraw = fn; // only swap in if the dry-run didn't explode
flashStatus("ok", "#3ddc84"); // green blip: it took
} catch (err) {
flashStatus(err.message, "#e63946"); // red blip: keep the old visual, show the error
}
}
That dry-run line is the sneaky-important bit. We test the new function once, on a throwaway canvas, before we trust it. If it throws during the test, we never assign it - so currentDraw still holds the last good version and the projector keeps glowing. This is the single habit that separates a nervous first gig from a relaxed one: you know, in your bones, that a fat-fingered block just does nothing instead of going black. Makes sense, right? The safety net is what lets you be brave.
The last piece is the gesture that fires it. Every live-coding editor binds "evaluate the block I'm in" to one chord - almost always Ctrl+Enter - so you never touch the mouse. Wiring it to a plain <textarea> is a handful of lines, and it turns any text box into a performance surface.
// turn a plain textarea into a live-coding surface: Ctrl+Enter evaluates the text.
// this one keystroke is the whole performance gesture - type, chord, watch it change.
const editor = document.querySelector("#editor"); // just a
editor.addEventListener("keydown", (e) => {
if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
e.preventDefault(); // don't insert a newline - we're evaluating
evaluateSafe(editor.value); // hand the whole text to our forgiving evaluator
}
});
That is the entire "IDE" a live coder needs to start: a text box and one key binding. No build step, no save button, no reload - you type, you chord, the running picture changes. Strip away all the fancy tooling and this is the loop you're actually living inside for a whole set.
One draw function is a sketch. Live performance wants more - you want to bring a new idea in over the old one, crossfade between two looks, build up layers like a DJ stacking loops. The trick scales beautifully: instead of one slot, keep a little list of active layers, each with its own opacity, and let the performer push and fade them.
// layers: a list of draw functions, each with an opacity you can fade.
// this is how you crossfade one visual into another, live.
const layers = [];
function pushLayer(codeString) {
const fn = new Function("ctx", "t", "size", codeString);
layers.push({ fn, alpha: 0, target: 1 }); // fades IN from 0 to 1
}
function renderLayers(ctx, t, size) {
for (const layer of layers) {
layer.alpha += (layer.target - layer.alpha) * 0.05; // ease toward target (episode 16!)
ctx.globalAlpha = layer.alpha;
layer.fn(ctx, t, size);
}
ctx.globalAlpha = 1;
// drop layers that have fully faded out, so the list stays tidy
for (let i = layers.length - 1; i >= 0; i--) {
if (layers[i].target === 0 && layers[i].alpha < 0.01) layers.splice(i, 1);
}
}
See what fell out for free? That alpha += (target - alpha) * 0.05 is the exact easing move from episode 16 - the one we used for smooth motion. Here it smooths a crossfade instead of a position, but it's the identical maths. That keeps happening in this craft: a trick you learned for one thing quietly turns out to be the answer somewhere completely different. A performer fades a layer out by setting its target to 0, and the same easing carries it gently to black before we splice it away. Now you can build, blend and drop looks live, wich is basically what VJing is.
Right, you understand the engine now, so - same advice as always - build the toy once to get it, then use a proper tool for real work. And in live visuals, the tool almost everyone reaches for in the browser is Hydra, by Olivia Jack. It's gorgeous, it runs in a plain web page, and it's built on a lovely idea: a video synthesiser you drive by chaining functions. Instead of a draw loop, you describe a signal - an oscillator, say - and then pipe it through transforms, exactly like plugging cables between modules on a hardware synth.
// Hydra: describe a visual signal, then CHAIN transforms onto it.
// osc() is an oscillator - moving stripes. the numbers are frequency, sync, colour-offset.
osc(20, 0.1, 1.2)
.rotate(0.4) // spin the whole field
.color(0.9, 0.2, 0.4) // tint it reddish
.out() // send it to the screen
That reads almost like English once your eye adjusts: make stripes, rotate them, tint them red, output. Every line you add is another module in the chain. And the whole thing is live by default - in Hydra, you edit a line, hit Ctrl+Enter, and that block re-evaluates instantly against the running output, which is precisely the hot-swap trick we just built by hand. You already understand the engine humming underneath it. That's why we built the toy first :-).
The reason Hydra (and analog video synths before it) can hypnotise you is two ideas: feedback and modulation. Feedback means the output feeds back into itself, so trails and tunnels and infinite regressions appear - we actually touched this way back in the shader feedback episode. Modulation means using one signal to distort another, like using a slow wave to wobble a fast pattern. In Hydra they're one-liners, and stacking them is where the "how is that just a few lines" magic lives.
// feedback: the previous frame bleeds into this one, making trails and tunnels.
// modulate: use one texture to warp another - the soul of organic, liquid motion.
osc(10, 0.1, 0.8)
.modulate(osc(3).rotate(0.2), 0.5) // a slow oscillator WARPS the fast one
.modulateRotate(osc(1), 0.3) // and slowly twists it too
.add(src(o0).scale(1.02), 0.6) // FEEDBACK: add a slightly-scaled copy of last frame
.out(o0)
That src(o0) bit is the feedback loop - o0 is the output buffer, and by adding a slightly-scaled copy of the last frame back into this one, you get an endless zoom-tunnel of trails. It's the same feedback principle from the shader trails episode, just handed to you as a word instead of a framebuffer dance. Honestly, half of learning Hydra is just typing weird combinations of modulate and add and going "ooh" - it rewards fiddling more than planning, and that playfulness is the whole point. This is an instrument, and you learn instruments by messing about.
Now let's connect two threads. When we did live coding music, the whole idea was sound you shape live. And back when we first did sound-reactive visuals, we pulled a frequency spectrum out of the microphone with an analyser. Put those together and you get the beating heart of an algorave: visuals that breathe with the music, driven live. Hydra has an audio analyser built right in, so a bass hit can shove a parameter in real time.
// Hydra's built-in audio: a.fft is an array of frequency-band energies (episode 19's spectrum!).
// bind a bass band to a visual parameter and the picture PUMPS with the kick drum.
a.setBins(4); // split the sound into 4 fat bands: bass..treble
osc(30, 0.1, 1)
.scale(() => 1 + a.fft[0] * 2) // bass (band 0) makes the whole thing PULSE bigger
.rotate(() => a.fft[3] * 3) // treble (band 3) adds shimmer/jitter
.out()
The little () => arrow functions are the key: instead of a fixed number, you hand Hydra a function it calls every frame, so the value is live - it re-reads a.fft[0] sixty times a second. That's the same analyser idea from episode 19, just wired into a live chain instead of a static sketch. The first time you do this and the visuals slam in time with a kick drum, it's genuinly a rush - the picture stops being a decoration playing next to the music and becomes something the music is pushing around. That's the difference between a screensaver and a performance.
Typing is expressive, but sometimes you want a physical knob to twist mid-set - it's faster and it looks better on stage than hunched over a keyboard. Remember when we wired up MIDI and OSC to connect real controllers? That exact skill plugs straight in here. A cheap MIDI knob box becomes your visual mixing desk: turn a dial, warp the picture. We use the same Web MIDI API from that episode.
// bind a physical MIDI knob to a live visual parameter - your hands on the visuals.
// this is the Web MIDI API from the MIDI/OSC episode, pointed at Hydra.
const knobs = {}; // knobs[controllerNumber] = 0..1
navigator.requestMIDIAccess().then((midi) => {
for (const input of midi.inputs.values()) {
input.onmidimessage = (msg) => {
const [status, cc, value] = msg.data;
if ((status & 0xf0) === 0xb0) { // 0xB0 = a "control change" (a knob turned)
knobs[cc] = value / 127; // MIDI values are 0..127, normalise to 0..1
}
};
}
});
// now use a knob LIVE inside a Hydra chain:
osc(40, 0.1, 1)
.modulate(noise(3), () => (knobs[1] || 0) * 0.8) // knob #1 controls the warp amount
.out()
Now knob #1 on a cheap little controller is the warp amount, live, no typing. Twist it during a breakdown and the whole field liquefies. This is where the episodes start clicking together like Lego: the MIDI skill from one episode, the sound analysis from another, the live-eval engine from this one, all feeding the same picture. None of it was wasted - it was all quietly building toward this, an instrument you play with your hands and your typing at the same time.
Let me talk about the un-glamorous stuff, because it's what actually makes or breaks a live set, and nobody tells you. First: big fonts, high contrast. Your code is projected; the back row has to read it, and you have to read it in a dark room with adrenaline running. Crank the editor font size way up before you start. Second: rehearse recovery, not just the good bits. Practise typing a block that crashes and calmly fixing it, because it will happen and the audience actually loves watching you recover. Third: keep a few known-good blocks saved somewhere you can paste from, so if your mind goes blank you've got a safety chord to hit.
// a tiny "safety chord" stash - known-good blocks you can paste when your brain freezes.
// on stage, adrenaline eats your memory. having escape hatches is not cheating, it's pro.
const safetyBlocks = {
calm: `osc(10, 0.05, 0.5).color(0.2, 0.4, 0.8).out()`,
build: `osc(30, 0.1, 1).modulate(noise(2), 0.4).rotate(0.2).out()`,
chaos: `osc(60, 0.3, 2).modulate(o0, 0.9).add(src(o0).scale(1.03), 0.7).out(o0)`,
};
// bind these to number keys 1/2/3 so one keystroke drops you into a safe, gorgeous state.
There's a real lesson hiding in that stash, and it's not just about panic. Building yourself a set of reliable go-to states, and knowing exactly what each one does, is how you get from "randomly flailing at the keyboard hoping something nice happens" to actually performing with intent. A musician practises scales so their fingers know the way home; these blocks are your scales. The freedom to improvise on stage comes from having that solid ground under you, not from throwing it away.
I want to sit with the deeper thing for a second, because live coding changed how I feel about all my code, not just the performance stuff. When your program never stops, you stop treating code as this precious artifact you perfect in private and then reveal finished. It becomes a conversation - a running thing you talk to, nudge, argue with, in real time. You get incredibly comfortable with change, with breaking things, with the idea that the current state is temporary and that's fine. That comfort with impermanence and mess is, weirdly, one of the healthiest things this whole craft has given me.
It also brutally clarifies what "good code" means when speed is everything. On stage you cannot afford a tangled function - you need small, sharp pieces you can recombine on the fly, named so clearly you can find them under pressure. Live coding is the most honest code review there is: if a chunk is too gnarly to change live, it's too gnarly, full stop. That instinct - keep the pieces small, keep them legible, keep them ready to change - is worth carrying back to every calm, un-projected project you'll ever write.
Here's the thing about performing live: you make something wonderful for four minutes and then it's gone. The exact chain of blocks that made the room gasp - could you find it again tomorrow? Probably not, and that starts to sting once you've made a few things you wish you'd kept. All this fast, throwaway, living code raises a quiet question we've been able to dodge until now: how do you keep the good stuff? How do you save a state, track what changed, go back to yesterday's version when today's edits ruined it? Every craft that moves fast eventually needs a way to remember where it's been, and creative coding is no different - so that's exactly where we head next. And a little further out, once you can keep your work, the natural itch is to make these instruments solid and shareable enough to hand to someone else - to turn a personal rig into a proper tool other people can pick up and play. Both of those grow straight out of tonight's set. So this week: build the little hot-eval engine above, get one visual crossfading into another, and if you've got Hydra open, bind a sound or a knob to it and just play for ten minutes. Screens up. That's the whole spirit :-).
new Function(...). That's all live coding is, under the polishosc().rotate().color().out(). Feedback (src(o0)) makes tunnels and trails; modulation uses one signal to warp another. It's the hot-swap engine you just built, dressed up beautifullya.fft is episode 19's frequency spectrum, and binding a bass band to a parameter with a () => live function makes the picture pump with the music. Screensaver becomes performanceSo that's the performance itch, finally scratched: our fast, knob-driven, systematised sketches grew up into instruments we can play in front of people, screens up, no hiding. And the wild part, as ever, is how little sits at the bottom of it - a loop calling a function in a slot, and the nerve to rewrite that slot while it runs. Build the tiny engine, get one crossfade working, bind a sound or a knob, and play for ten minutes without stopping. That non-stop, talking-to-your-code feeling is what tonight was really about, and once you've felt it you'll want it in everything. Merci for reading, and go make some noise with your pixels :-).
Sallukes! Thanks for reading.
X