readout
A CMOS rolling shutter for Resolume

What it does
An FFGL effect for Resolume Arena and Avenue. A CMOS sensor exposes and reads its rows one after another rather than taking the frame all at once. Row r of H is read Readout × (1 − r / H) before the frame's timestamp, and was integrating light for Exposure before that. So every pixel has a sample window, and the picture gets sampled there. That's the whole plugin.
None of what follows is drawn. A vertical bar moving at v px/frame leans by v × Readout, because the top of the frame was read earlier and shows the bar further back. Shake the camera and every row sees it in a different place, so a straight edge comes out as a wave, one full cycle every 1/f of readout. A strobe shorter than the readout only lights the rows whose exposure window overlapped it, so the band is (Exposure + Length) / Readout of the frame high, its edges ramp over the shorter of the two, and where it sits is set by the flash's phase. A mains lamp is brightest twice a cycle, so at 50 Hz it pulses at 100 Hz and beats against the readout into rolling dark bands T_light / Readout apart, which is why phone video of a stage looks stripy. The propeller, partial frames and every other rolling-shutter photo anybody's ever posted need no code at all: they're the first item on that list applied to something that happens to be spinning.
Global sets the readout to zero, a global shutter for comparison, and with nothing else switched on it hands the picture back untouched, to the byte. That's checked against a negative control, so the check is able to fail.
The shake moves a model of the camera rather than wobbling the picture. It has an audio input, because a speaker near a camera moves the camera, so the audio drives the camera's pose and the jello follows by itself. Audio Drive defaults to zero, so with nothing routed the camera sits still. The flash fires from a button, from Resolume's own beat or bar, from an onset in the routed audio, or on an interval.
The limit is that a real sensor samples a continuous scene, and this one only has the frames the host gave it. Between two of them it either interpolates or holds the older one, so the lean of a moving bar is right, but the sub-frame detail of anything moving unevenly is made up by the interpolation rather than observed. The shake is sampled once per row rather than integrated across the exposure. Rows are what make jello, so that part's exact, but a real sensor would blur the shake as well and this doesn't.
There's also an OpenFX plugin for DaVinci Resolve, Vegas Pro, Nuke and Natron, where each row reads earlier frames of the clip itself rather than a ring. The audio controls and beat triggers are Resolume-only, and Fire becomes a keyframeable Fire At time.
- Readout time and exposure in real milliseconds, with the row's sample window as the one rule underneath everything
- Four readout directions — top-down, bottom-up, left-right, right-left
- Global shutter for comparison, which returns the input bit-exactly
- Flash banding whose height, edge ramp and position come from exposure, length and phase rather than a drawn band
- Mains flicker at 50 or 60 Hz, with the band spacing the beat against readout gives
- Camera shake driven by audio level, because a speaker near a camera moves the camera
- Flash triggering from a button, Resolume's beat or bar, an audio onset, or an interval
- Blend or Hold between the host's frames, so the interpolation is a choice rather than a hidden assumption
- Universal macOS bundle and a Windows x64 DLL
Where it stands
v0.2.0, which adds the OpenFX build, and early days. The OpenFX build has been checked in DaVinci Resolve Studio 21.1 on macOS; Vegas, Nuke and Natron are untried. On 21 September 2026 the x64 DLL was registered, loaded and instantiated in Resolume Arena 7.27.1 on win-lab: SW Readout is listed under idstring RO01 among 112 video effects, the shaders compiled, and Arena drew its inspector. That VM has no GPU (OpenGL came from Mesa llvmpipe, a software rasteriser), so it's never run on a GPU in Resolume, it's never been instantiated in Arena on macOS, and nothing was timed on Windows at all. That session is where the float-overflow trap in Resolume's clock turned up, and Arena is the first real host the unit detection has met. No real audio has reached it in a host, so what Resolume's 64 FFT bins mean is still an assumption, and Beat and Bar have only seen a synthetic 120 bpm transport. There's been no long session, no composition save and reload and no preset recall in the host, and there's no user guide and no factory presets. Every claim is checked against its closed form by an offline harness driving the real plugin, and tools/verify.sh runs the lot against a fresh universal Release build: the bar's lean, the flash band's height and position, the mains bands and the shake's period and swing all land on their closed forms, and Global matches the input to the byte while its negative control doesn't, so that check can fail. All 20 swept controls change the picture, and all three shaders compile through glslc, not just Apple's driver. On macOS it costs 0.278 ms a frame at 720p, 0.517 at 1080p, 0.902 at 1440p and 2.274 at 4K. The browser demo is live, but it's a port and not evidence: the GLSL from source/Shaders.cpp copied across unedited and run in WebGL2 over clips made in the page, without the audio side or Trigger's Beat and Bar, because a browser has nothing like Resolume's FFT parameter or its transport. Nothing on that page measures anything; the numbers come from the harness in the repository. Try it in your own rig before you trust it in a show.
Watch it running
Nothing loads from YouTube until you press play.Watch it there instead