// Glitch family: video feedback. A camera pointed at its own monitor — the // frame is redrawn inside itself slightly smaller and slightly turned, so a // small motif at the centre becomes an endless corridor of copies of itself. // // Time Smear is the other feedback glitch and it TRANSLATES the previous frame, // which leaves a comet trail behind a moving subject. This scales it. The // difference is the whole scene: a translated feedback drifts off the edge and // clears, a scaled one never clears, because everything the frame has ever // contained is still in there, one ring further in. // // A live motif is drawn every frame, so the image exists before the loop has // anything in it and comes back after a seek. // // No declared slow axis. What the corridor holds depends on where the loop has // been, so two ten-second windows of the same parameters are 0.020 apart on // their own and the persistence axis moved 0.004 — the same measurement Turing // Bloom and Voronoi Shatter record. A feedback scene's history is its structure, // and Phase 11 cannot separate a parameter from it. export const drosteFeedback = { name: 'Droste Feedback', family: 'glitch', kind: 'fragment', texture: 0.5, consumes: ['ink'], traits: ['camera', 'style'], params: { zoom: { type: 'float', range: [0.93, 1.02], default: 0.975, uniform: 'u_zoomStep' }, turn: { type: 'float', range: [-0.12, 0.12], default: 0.04, uniform: 'u_turn' }, persist: { type: 'float', range: [0.9, 0.995], default: 0.985, uniform: 'u_persist' }, motif: { type: 'float', range: [0.04, 0.35], default: 0.14, uniform: 'u_motif' }, tint: { type: 'float', range: [0, 0.5], default: 0.15, uniform: 'u_tint' }, pace: { type: 'float', range: [0.05, 0.8], default: 0.25, uniform: 'u_pace', bias: 'motion', rate: true }, palette: { type: 'palette', count: 5 }, }, reactive: { motif: { feature: 'beat', amount: 0.2, response: 'spike' }, }, shader: ` vec4 scene(vec2 uv, vec2 p) { float t = u_time * u_pace + u_seed; // Folded as well as filmed: a feedback loop through a mirror is still a // feedback loop, and the fold is the track's, not this scene's. p = sigFolded(sigCamera(p)); // The motif. Small, live, and always drawn: this is the thing the loop eats. vec2 at = vec2(sin(t * 0.6), cos(t * 0.47)) * 0.42; float d = length(p - at) - u_motif; float ring = abs(d) - u_motif * 0.35; vec3 col = pal(0) * 0.05; col += palRamp(0.2 + fbm(p * 2.0 + t, 3) * 0.2) * smoothstep(0.02, -0.02, ring) * (0.5 + 0.6 * 0.8); col += pal(4) * inkStroke(ring) * (0.3 + 0.6 * 0.9); // The loop: read the previous frame from a slightly LARGER, slightly turned // sample of itself, so last frame's image comes back shrunk toward the // middle. Contracting is what nests — expanding pushes every copy off the // edge and leaves a plume instead of a corridor. Scaling about the centre in // screen space is what keeps the copies concentric rather than sliding. vec2 c = uv - 0.5; c.x *= u_aspect; c = rot(u_turn) * c / max(u_zoomStep, 0.5); c.x /= u_aspect; vec3 past = prev(c + 0.5); // Each generation is tinted a step further round the palette, so depth into // the corridor is legible as colour rather than only as size. past = mix(past, palRamp(0.6) * (past.r + past.g + past.b) * 0.5, u_tint); col = max(col, past * u_persist); // Vignette, which also stops the corner content being recycled forever. col *= 0.75 + 0.25 * exp(-dot(p, p) * 0.35); return vec4(inkValue(col), 1.0); } `, }; export default drosteFeedback;