2026-03-17
What happened
Added LuaJIT (with Lua 5.4 fallback). A patch is a plain .lua file loaded at startup with -s patch.lua. The engine calls on_frame(dt) every render frame and on_osc(addr, arg) for each incoming OSC event.
The vj.* API exposed to Lua covers everything: vj.audio(i), vj.image(i), vj.video(i), vj.gain(f), vj.stop() for playback; vj.sample(i) and vj.fft(i) for raw audio data; vj.shader(i) and vj.uniform(i, v) for shader control; and vj.image_pixel(clip, x, y) for per-pixel CPU reads from loaded images.
This makes a real reactive patch possible: detect bass transients in on_frame, cut to a random video on each beat, drive shader speed from FFT bins. The example patch does exactly that — it accumulates bass energy, fires a cut when it crosses a threshold, and enforces a 2-second cooldown to avoid strobing.
All vj.* calls from Lua write to the same atomic queue as incoming OSC, so they're safe to call from either callback without locking. Changes take effect on the next frame.
commits: 0d91731