A camera recorded one column at a time, until the screen's width becomes a timeline.
A normal photograph captures one instant across all of space. A slit scanner does the opposite: it captures one thin column of space, over and over, and lays each capture down beside the last. The picture you end up with is mostly made of the past — its left edge is older than its right. Anything that holds still draws as smooth horizontal streaks; anything that moves smears into a wave. This one looks through your webcam if you let it — hit Enable camera on the render panel. Otherwise a simple bobbing shape stands in, which scans just as well.
The trick needs a
The buffer is almost entirely lazy. Every frame it copies its previous self straight through — each column keeps exactly what it already held. The only exception is the narrow
iChannel1 is Buffer A reading itself from last frame — the memory. Sampling it at the same uv and writing it back is a perfect copy; the mix only diverges where write is 1, inside the scan band.
vec3 frozen = texture(iChannel1, uv).rgb; // last frame, this column
vec3 now = camera(uv); // the live subject
float write = step(abs(uv.x - scanline), band);
fragColor = vec4(mix(frozen, now, write), 1.0);The band is sized to be at least as wide as the head travels in one frame — max(1.5*px, 0.5*speed*px + px) — so cranking the speed up never tears gaps between the columns it stamps.
Both passes sample the subject through one helper. When the camera is granted, the engine binds the live video to iChannel0 and reports its size in iChannelResolution[0]; when it isn’t, that size reads 0 and we draw a moving stand-in instead — so the shader is identical whether or not a camera exists.
bool camLive() { return iChannelResolution[0].x > 0.5; }
vec3 camera(vec2 uv) {
return camLive() ? texture(iChannel0, uv).rgb : fallback(uv);
}If we only ever showed the frozen buffer, you’d never see what’s about to be recorded. So the display pass keeps the recorded past to the left of the head untouched, and to the right it fades the live subject back in — strong at the head, dissolving into the recording as you look further ahead. The width of that dissolve is the
float d = abs(uv.x - scanline);
if (uv.x > scanline) col = mix(col, live, smoothstep(feather, 0.02, d));
col += seam * smoothstep(px * 1.5, 0.0, d); // the scan-head lineThe last line adds a thin highlight right at the head — the
Slow the scan right down and hold still: you’ll watch yourself get painted, strip by strip, into a portrait that’s a few seconds wide. Wave an arm across the frame and it stretches into the long diagonal that gives slit-scan photographs their melting, time-warped signature.