// Glitch family: tape. The transport is not quite steady, so every line of the // image is displaced by where the tape was when that line was written — a // continuous horizontal warp, chroma trailing behind luma, and the occasional // dropout where the oxide has worn through. // // Scan Tear and Block Mosh are digital faults: they are quantised, blocky, and // they happen to whole regions at once. This is analogue and wet — nothing has // an edge, the error varies smoothly down the frame, and the colour lags the // picture instead of being replaced by it. Wow and flutter, not corruption. // // No declared slow axis: the dropouts and the head smear put two windows of // identical parameters 0.041 apart, the second-highest noise floor in the // library, and chroma bleed against that measured 0.02x. export const analogWow = { name: 'Analog Wow', family: 'glitch', kind: 'fragment', texture: 0.9, consumes: ['ink'], traits: ['camera', 'style'], params: { wow: { type: 'float', range: [0, 0.12], default: 0.035, uniform: 'u_wow' }, flutter: { type: 'float', range: [0, 0.03], default: 0.008, uniform: 'u_flutter' }, bleed: { type: 'float', range: [0, 0.05], default: 0.015, uniform: 'u_bleed' }, dropout: { type: 'float', range: [0, 0.6], default: 0.2, uniform: 'u_dropout' }, bars: { type: 'float', range: [1, 9], default: 3.5, uniform: 'u_bars', bias: 'density' }, speed: { type: 'float', range: [0.05, 0.7], default: 0.2, uniform: 'u_speed', bias: 'motion', rate: true }, palette: { type: 'palette', count: 5 }, }, reactive: { dropout: { feature: 'flux', amount: 0.3, response: 'spike' }, wow: { feature: 'bandLow', amount: 0.25, response: 'smooth' }, }, shader: ` // What was recorded: broad soft bars of colour, the kind of thing that shows // tape damage clearly because it has no detail of its own to hide it behind. vec3 programme(vec2 q, float t) { float band = q.y * u_bars + fbm(q * 1.4 + vec2(t * 0.3, 0.0), 3) * 1.2; vec3 col = palRamp(0.1 + band * 0.12); col *= 0.55 + 0.45 * sat(0.5 + 0.5 * sin(band * 3.14159 + t)); col += pal(4) * smoothstep(0.85, 1.0, sat(0.5 + 0.5 * sin(q.x * 3.0 - t * 1.3))) * 0.25; // The boundary between bands is drawn in the track's weight, so even a // damaged signal is damaged in the video's own hand. col += pal(4) * inkStroke(fract(band) - 0.5) * (0.15 + u_sigLine * 0.9); return col * 0.7; } vec4 scene(vec2 uv, vec2 p) { float t = u_time * u_speed + u_seed; p = sigCamera(p); // Transport error for THIS line: a slow wow, a faster flutter, and a little // noise on top. It is a function of y, which is what makes the frame skew // rather than slide — every line was written at a different moment. float line = p.y * 30.0; float err = sin(p.y * 2.1 - t * 1.7) * u_wow + sin(p.y * 11.0 + t * 5.3) * u_flutter + (vnoise(vec2(line, t * 3.0)) - 0.5) * u_flutter * 2.0; vec2 q = p + vec2(err, 0.0); // Luma is where it should be; chroma trails it. Sampling the programme // three times at three displacements is exactly what a chroma delay does. vec3 col; col.r = programme(q + vec2(u_bleed, 0.0), t).r; col.g = programme(q, t).g; col.b = programme(q - vec2(u_bleed * 0.7, 0.0), t).b; // Dropouts: bands where the tape has lost contact. They travel slowly down // the frame, and they replace the picture with the tape's own noise floor // rather than with black — a black band is a cut, this is a fault. float bandId = floor(p.y * 8.0 + t * 2.0); float hit = step(1.0 - u_dropout * 0.35, hash11(bandId * 1.7 + floor(t * 3.0))); float band = smoothstep(0.5, 0.15, abs(fract(p.y * 8.0 + t * 2.0) - 0.5)) * hit; vec3 noise = pal(1) * (0.25 + 0.5 * hash12(vec2(uv.x * 400.0, bandId))); col = mix(col, noise, band * 0.7); // Head smear: the previous frame, dragged sideways by the same transport // error, is what gives tape its characteristic horizontal ghosting. The // programme above stands on its own, so a seek recovers immediately. vec3 past = prev(uv - vec2(err * 0.4 + 0.004, 0.0)); col = mix(col, max(col, past), 0.7 * 0.55); // Head-switching noise at the bottom of the frame — the one part of the // image that is always damaged. float sw = smoothstep(0.06, 0.0, uv.y) * u_dropout; col = mix(col, pal(2) * hash12(vec2(uv.x * 300.0, floor(t * 12.0))), sw * 0.6); return vec4(inkValue(col), 1.0); } `, }; export default analogWow;