Hydra: live-coding video-synth visuals
Learning objectives
- learner can model Hydra as analog video synthesis and chain its five function types from source to .out()
- learner can build patches with sources, geometry/colour transforms, blends, and modulation warps
- learner can animate any parameter with time and functions, and route across multiple output buffers
Capstone — one whole task that evidences the objectives
Perform-code a live Hydra patch that layers multiple sources across output buffers, applies geometry and colour transforms, modulates one source by another, and animates parameters with time functions — sharing the sketch as a remixable link.
Prerequisite modules
This module builds toward the core skill of the algorave VJ booth: improvising visuals in a browser tab at hydra.ojack.xyz, projected behind a live-coded music set, where every edit is executed in front of an audience with Ctrl+Shift+Enter and there is no undo — only the next re-evaluation. Hydra rewards performers who think in signal flow rather than draw calls, which is why the module opens with the mental model: Hydra as an analog video synthesizer, where each function is a box that generates or transforms a signal, chained left-to-right like patch cables.
The scaffolding arc starts supported: type osc().out() and get scrolling stripes, then follow “Every Hydra patch runs source → geometry/color transforms → .out()” to extend one chain, using the five-function-type taxonomy to predict where any unfamiliar function can legally sit. Next comes two-texture work — blends to composite layers by colour, then the conceptual leap of modulation, where one texture’s channels warp another’s coordinates (“modulation is to geometry what blending is to colour”). Then parameters come alive: “Any Hydra parameter can be a function of time” replaces static knob positions with per-frame expressions. Finally, the four output buffers turn single chains into a performable rig — build on a hidden buffer, cross-mix with src(), switch what is rendered.
The required atoms gate the capstone directly: without the pipeline discipline, the transform vocabulary, modulate, time functions, buffer routing, and the share-link workflow, the performance cannot be executed or published as a remixable sketch. Supporting atoms enrich the picture — restatements of the five-type taxonomy, the modulate-as-warp mechanism, and the parameters-are-functions idea from complementary angles, plus the canvas-versus-signal-flow contrast, per-pixel RGB arithmetic behind blends, abstraction as a principle, and why compiled GPU shaders keep long chains cheap — deepening intuition without being load-bearing for the task.
Walkthrough
Open the Hydra editor — nothing to install. Evaluate the whole sketch with Ctrl-Shift-Enter; hush() clears the screen. Think signal flow, not drawing: each function is a box, chained left-to-right like patch cables ([[hydra-analog-video-synthesis-model]]). We build the capstone one link at a time; each step is a complete, runnable patch.
1 — first signal. A source generator and an output. osc(10, 0.1, 0.8) is an oscillator — frequency 10, a slow scroll, medium colour offset — sent to the screen with .out(). You get moving stripes. That source → .out() shape is every patch’s skeleton ([[hydra-source-geometry-color-pipeline]]).
osc(10, 0.1, 0.8).out()
2 — a geometry transform. Add a box to the chain. .kaleid(4) folds the stripes into 4-fold radial symmetry — a geometry transform reshapes where pixels sit ([[hydra-geometry-transforms]]). It’s legal here because Hydra’s five function types have a fixed pipeline order.
osc(10, 0.1, 0.8).kaleid(4).out()
3 — colour, deliberately. .color(1, 0.7, 0.2) multiplies the RGB channels toward warm amber — one intentional palette move, not a rainbow ([[hydra-color-transforms]]). Restraint here is the whole difference between composed and garish.
osc(10, 0.1, 0.8).kaleid(4).color(1, 0.7, 0.2).out()
4 — modulation (the conceptual leap). .modulate(noise(3), 0.3) uses a second texture — slow noise — to warp the first texture’s coordinates. Modulation is to geometry what blending is to colour: one signal displaces another ([[hydra-modulation]]). This is where a flat pattern becomes an organic, moving field.
osc(10, 0.1, 0.8).kaleid(4).color(1, 0.7, 0.2).modulate(noise(3), 0.3).out()
5 — animate with time. Any parameter can be a function of time instead of a fixed number. .rotate(() => time * 0.1) turns the whole frame slowly, forever — per-frame motion with no keyframes ([[hydra-time-function-parameters]]).
osc(10, 0.1, 0.8).kaleid(4).color(1, 0.7, 0.2).modulate(noise(3), 0.3).rotate(() => time * 0.1).out()
6 — two buffers, composited (the capstone). A performable rig builds sources on hidden buffers and mixes them. Render the noise field to buffer o1, then feed it into the main chain with src(o1) and show the result on o0 — two sources, geometry + colour transforms, one modulating the other, animated by time ([[hydra-output-buffers]]):
noise(3, 0.2).out(o1)
osc(20, 0.1, 0.8).kaleid(4).color(1, 0.7, 0.2).modulate(src(o1), 0.4).rotate(() => time * 0.1).out(o0)
What good looks like. A frame with a clear focal structure (the kaleidoscope centre), a restrained palette (one warm hue, not full colorama), and motion you can follow rather than seizure-inducing flicker. One coherent noise source doing the warping reads as organic; stacking three fights itself ([[one-coherent-noise-source-discipline]]). If it looks like busy soup, pull a transform out — subtraction is composition. (Skill map: live-visualist Domain B — focal hierarchy, restrained palette, motion you can read.)
Now make it yours. Change kaleid(4) to kaleid(6) or remove it for raw stripes. Swap noise(3) for voronoi(4) as the modulator. Push the modulation amount 0.4 → 0.8 and watch it dissolve. Animate the hue instead: add .hue(() => time * 0.1). Build a third source on o2 and blend it in with .blend(src(o2), 0.3).
Runnable examples
Generated from the context/ instrument corpus by concept (redistributable idioms only). Do not edit — regenerate with gen-module-examples.mjs.
modulation-warp
osc(4).modulate(src(o0), 0.6).out(o0)
hydra-0022 · CC0-1.0
s0.initP5(); src(s0).modulate(noize(), 0.3).out()
p5live-0038 · CC0-1.0
feedback-trail
osc(4).modulate(src(o0), 0.6).out(o0)
hydra-0022 · CC0-1.0
function draw(){ fill(0, 20); rect(0, 0, width, height); circle(mouseX, mouseY, 40) }
p5live-0003 · CC0-1.0
radial-symmetry
osc(10).kaleid(5).out()
hydra-0010 · CC0-1.0
// sandbox
osc(10, 0.05, 1.3).kaleid(8).out()
// sandbox
p5live-0037 · CC0-1.0
noise-field
noise(4, 0.1).out()
hydra-0002 · CC0-1.0
float h21(vec2 p){return fract(sin(dot(p,vec2(12.9898,78.233)))*43758.5453);}
glsl-0013 · public-domain
palette-cycle
osc(30, 0.1, 1).colorama(0.1).out()
hydra-0015 · CC0-1.0
hsvrgb [fract (ft/6.28 + 0.1*time), 1, 1] >> rgb
punctual-0024 · CC0-1.0
oscillator-texture
osc(10).out(o1)
hydra-0039 · CC0-1.0
float v = sin((st.x + u_time*0.1) * 40.0) * 0.5 + 0.5;
glsl-0024 · public-domain
hue-shift
osc(30).hue(() => time * 0.1).out()
hydra-0016 · CC0-1.0
hsvrgb [fract (ft/6.28 + 0.1*time), 1, 1] >> rgb
punctual-0024 · CC0-1.0
pixelate
osc(20).pixelate(20, 20).out()
hydra-0014 · CC0-1.0
vec2 q = floor(st * blocks) / blocks;
glsl-0027 · public-domain
polar-warp
float r = length(uv); float a = atan(uv.y, uv.x);
glsl-0008 · public-domain
[rtx [fr, ft + 0.2*time], rty [fr, ft], 0.5] >> rgb
punctual-0032 · CC0-1.0
threshold-mono
(osc (40*fx) > 0.5) >> rgb
punctual-0027 · CC0-1.0
noise(4).luma(0.4, 0.1).out()
hydra-0019 · CC0-1.0
Atoms in this module
Required — these gate the capstone
Supporting — enrichment, not gating
Part of curricula
- Audio-Visual Performer — integrated, synced live AV — Pair sound and image (unsynced, side by side) required
- Live Visualist — zero to performing live-coded & generative visuals — Generative canvas — colour, motion, and Hydra live-coding required
- VJ — visual performance with projection, light & video — Generate & compose: build your own look required
Unlocks — modules that require this one