Hydra gamepad patching is the cheapest VJ rig you can put together this weekend. Olivia Jack's Hydra is a live-coded video synth that runs in a browser tab — feedback loops, oscillators, rotations, all expressed as chained JavaScript. Plug a DualSense into Universal Controller MIDI, paste fifteen lines of glue, and every CC becomes a Hydra-native function. Sweep the stick, the kaleidoscope warps. No GPU plugin, no compile step, no licence fee.
- What you do: bridge gamepad → MIDI, paste a WebMIDI helper into Hydra, wrap any numeric arg in
() => cc(n). - What you need: Hydra (browser), Universal Controller MIDI, any USB-C gamepad.
- Time: 10 minutes to a working patch.
- Cost: $89 for the bridge. Hydra is free.
Why Hydra is the right canvas for gamepad VJing
Hydra is a thin JavaScript wrapper around WebGL. Every visual operation — osc(), noise(), shape(), rotate(), modulate() — takes a number or a function returning a number. That second form is the door. You can pass a closure, Hydra evaluates it every frame, and the visual reacts in real time. Throw a live MIDI value behind that closure and the gamepad is the modulation source. Resolume needs a cue grid. TouchDesigner needs a CHOP network. Hydra needs one closure.
The WebMIDI bootstrap
Paste this once at the top of your Hydra session. It requests MIDI access, listens to every input port, stores the last value for every CC, and hangs a cc(n) helper on window so Hydra can see it.
// Hydra ⇄ WebMIDI bridge — paste once per session
const _ccVals = new Array(128).fill(0);
navigator.requestMIDIAccess().then(midi => {
for (const input of midi.inputs.values()) {
input.onmidimessage = (e) => {
const [status, data1, data2] = e.data;
if ((status & 0xf0) === 0xb0) _ccVals[data1] = data2 / 127;
};
console.log('Listening on', input.name);
}
});
// Returns 0..1 for any CC number. Default 0 until first message lands.
window.cc = (n) => _ccVals[n] ?? 0;
// Bipolar variant for sticks — returns -1..1, centred at 0.5 input.
window.bcc = (n) => (_ccVals[n] - 0.5) * 2;
Hit Ctrl-Shift-Enter to evaluate. The console should print every MIDI input the bridge exposes. Now wiggle a stick — cc(0) in the console will return live values.
Your first gamepad-driven patch
The Hydra "hello world" is osc().out(). Add a stick and a trigger to push it into chaotic territory.
// Left stick X drives oscillator frequency.
// Right trigger drives rotation speed.
// Face button group cycles colour.
osc(
() => 10 + cc(0) * 200, // freq: stick X
0.1,
() => cc(7) // colour: trigger
)
.rotate(() => bcc(1) * 3.14) // stick Y bipolar → -π..π
.modulate(noise(3, 0.1), () => cc(6) * 0.5)
.out();
Evaluate the block. Stick X stretches the bars. Stick Y rotates the whole frame. L2 deepens the noise modulation. R2 walks the colour wheel. You just built a VJ instrument in eight lines.
Default CC map for the gamepad
Universal Controller MIDI ships with a sensible default layout. Memorise these — every Hydra patch below uses them.
| Input | CC | Type | Hydra use |
|---|---|---|---|
| Left stick X | CC 0 | Bipolar | osc() frequency, rotate() angle |
| Left stick Y | CC 1 | Bipolar | scale() amount, kaleid() sides |
| Right stick X | CC 2 | Bipolar | modulate() amount |
| Right stick Y | CC 3 | Bipolar | repeat() count |
| L2 trigger | CC 6 | Unipolar | Noise depth, blur amount |
| R2 trigger | CC 7 | Unipolar | Colour shift, contrast |
| Touchpad X | CC 16 | Absolute | XY pad horizontal |
| Touchpad Y | CC 17 | Absolute | XY pad vertical |
| Face buttons | Note 36–39 | Discrete | Patch switcher |
Button-driven patch switching
Buttons send notes, not CCs. The bridge can be set to translate them to a 0/1 CC for Hydra — easier to consume.
// Enable "buttons as CC" in the bridge — face buttons land on CC 100..103.
// Then chain a conditional inside a closure:
const patch = () => {
if (cc(100) > 0.5) return osc(40, 0.1, () => cc(7)).kaleid(5);
if (cc(101) > 0.5) return noise(() => cc(0) * 10).colorama(0.4);
if (cc(102) > 0.5) return voronoi(() => cc(2) * 20).pixelate(20, 20);
return shape(4).repeat(() => 2 + cc(3) * 6);
};
// Wrap in a render call. Hydra re-evaluates the closure every frame.
solid()
.layer(patch().luma(0.1))
.out();
Hit Square — kaleidoscope mode. Hit Cross — voronoi. Hit Circle — coloured noise. The closure runs at 60 fps, so the switch is instant.
Touchpad as a true XY surface
The DualSense touchpad is the closest thing to a Kaoss Pad you'll ever get for free. CC 16 and CC 17 are absolute positions across the whole pad. Drop them into a 2D modulation chain and the touchpad becomes the patch.
// Touchpad = XY pad. Click locks the current spot.
osc(20)
.rotate(() => cc(16) * 6.28)
.modulate(
noise(() => 1 + cc(17) * 8),
() => cc(16) * 0.7
)
.colorama(() => cc(17))
.out();
See the touchpad XY guide for the calibration step. For latency comparisons across controllers, the latency benchmark is worth a read before you book a gig.
Tradeoffs you should know
Hydra runs in a browser, which means it shares the GPU with everything else. Don't VJ from the same browser instance that has Slack open. Hydra also has no built-in clip launcher — if you need cue lists and a timeline, Resolume is the grown-up tool. Hydra's superpower is mutability: change the patch live, mid-set, with the audience watching the code. Plug a gamepad in and you stop typing during the drop.
The whole rig is a $89 bridge, a free browser tool, and a controller you already own. Grab Universal Controller MIDI and start patching.