// Glitch family: a stack of oscilloscope traces losing signal. // // Distinct from Scan Tear (rows displaced sideways), Block Mosh (block-level // datamosh) and Pitch Shatter (vertical transposition): nothing here is // displaced at all. The corruption is in the SIGNAL — each trace degrades from a // clean wave into noise as its lock is lost, and regains it. Loss of lock steps // on the bar grid, so traces drop out in time rather than flickering. export const signalDecay = { name: 'Signal Decay', family: 'glitch', kind: 'fragment', traits: ['camera', 'style'], params: { traces: { type: 'int', range: [2, 10], default: 5, uniform: 'u_traces', bias: 'density' }, amplitude:{ type: 'float', range: [0.02, 0.3], default: 0.1, uniform: 'u_amplitude', bias: 'energy' }, frequency:{ type: 'float', range: [1, 22], default: 7, uniform: 'u_frequency', bias: 'density' }, loss: { type: 'float', range: [0, 0.9], default: 0.35, uniform: 'u_loss' }, hiss: { type: 'float', range: [0, 1], default: 0.4, uniform: 'u_hiss' }, persist: { type: 'float', range: [0, 0.85], default: 0.4, uniform: 'u_persist' }, speed: { type: 'float', range: [0.1, 2.0], default: 0.6, uniform: 'u_speed', bias: 'motion', rate: true }, palette: { type: 'palette', count: 5 }, }, reactive: { amplitude: { feature: 'bandLow', amount: 0.35, response: 'smooth' }, hiss: { feature: 'flatness', amount: 0.3 }, }, shader: ` vec4 scene(vec2 uv, vec2 p) { float t = u_time * u_speed + u_seed; p = sigCamera(p); // Quantised era: lock is lost and regained on the eighth-bar grid, so // dropouts land with the music instead of crawling. float era = floor(u_barPhase * 8.0) + floor(t * 2.0) * 8.0; vec3 col = pal(0) * 0.05; float span = 2.0 / max(float(u_traces), 1.0); for (int i = 0; i < 10; i++) { if (i >= u_traces) break; float fi = float(i); float centre = -1.0 + span * (fi + 0.5); float s = u_seed + fi * 27.7; // How much lock this trace has this era. Below zero it is pure noise. float lock = sat(hash12(vec2(fi, era)) * 1.4 - u_loss); // Clean signal: two sines and a slow envelope, so it looks like a // waveform rather than a test tone. float clean = sin(p.x * u_frequency + t * 3.0 + s) * 0.6 + sin(p.x * u_frequency * 2.7 - t * 1.7 + s * 1.3) * 0.4; // Noise floor: hashed per pixel column and era, held steady within a // step so it reads as static rather than as a shimmer. float noise = (hash12(vec2(floor(p.x * 220.0), era + fi)) - 0.5) * 2.0; float signal = mix(noise, clean, lock); float y = centre + signal * u_amplitude; float d = abs(p.y - y); float w = 0.004 + u_sigLine * 0.012; float line = smoothstep(w * 2.5, 0.0, d); vec3 tint = palRamp(fract(s) * 0.4 + 0.15); col += tint * line * (0.4 + lock * 0.6); col += tint * exp(-d * 40.0) * 0.25 * lock; // Hiss band around an unlocked trace: the visual equivalent of the // sound. Confined to the lane, so it never washes the whole frame. if (lock < 0.4) { float band = exp(-abs(p.y - centre) * 12.0); col += pal(3) * band * abs(noise) * u_hiss * 0.3 * (1.0 - lock); } } // Ghost of the previous frame, so a dropout leaves a trail rather than // vanishing cleanly. col = max(col, prev(uv) * u_persist); col += sigGrain(uv); return vec4(col, 1.0); } `, }; export default signalDecay;