// Glitch family: vertical transposition. The frame is cut into vertical bands, // each of which shifts up or down by a small amount. That alone is a tear; what // makes it churn is that the offset is re-rolled on the quantised grid and the // previous frame's transposed image is smeared underneath — so slices translate // to a new fixed pose each step instead of crawling, and the pitch reads as an // edit rather than a broken renderer. export const pitchShatter = { name: 'Pitch Shatter', family: 'glitch', kind: 'fragment', // Personality: see look/Personality.js. traits: ['camera', 'style'], params: { slices: { type: 'float', range: [4, 40], default: 18, uniform: 'u_slices', bias: 'density' }, amp: { type: 'float', range: [0, 0.35], default: 0.1, uniform: 'u_amp', bias: 'energy' }, quantize: { type: 'float', range: [1, 8], default: 4, uniform: 'u_quantize' }, bleed: { type: 'float', range: [0, 0.9], default: 0.4, uniform: 'u_bleed' }, glow: { type: 'float', range: [0, 1], default: 0.3, uniform: 'u_glow', bias: 'energy' }, speed: { type: 'float', range: [0.05, 0.8], default: 0.25, uniform: 'u_speed', bias: 'motion', rate: true }, palette: { type: 'palette', count: 5 }, }, reactive: { amp: { feature: 'beat', amount: 0.4, response: 'spike' }, bleed: { feature: 'flux', amount: 0.25, response: 'smooth' }, glow: { feature: 'bandHigh', amount: 0.2 }, }, shader: ` vec4 scene(vec2 uv, vec2 p) { float t = u_time * u_speed + u_seed; p = sigCamera(p); // One shared grid for every discontinuity below: slices re-roll on the // quantised step rather than moving continuously frame-to-frame. float q = max(u_quantize, 1.0); float step_ = floor(u_barPhase * q) + floor(t * 6.0) * q; // A vertical slice is a column of pixels; each gets one fixed offset per // step, decided by a hash of (slice, step) so it jumps cleanly on the grid. float slice = floor(uv.x * u_slices); float sliceRand = hash12(vec2(slice, step_ * 31.0)); float pitch = (sliceRand - 0.5) * 2.0 * u_amp; // The base image is always sampled from the un-pitched field so the scene // has real content behind the displacement, even on its first frame. vec2 baseUv = vec2(uv.x, fract(uv.y - pitch)); float field = fbm(vec2(baseUv.x * 2.5, baseUv.y * 4.0) + t * 0.4, 4); float ramp = fract(field * 2.0 + baseUv.y * 2.0 - t * 0.4); vec3 col = palRamp(ramp * 0.7 + slice * 0.02); col *= 0.4 + 0.6 * smoothstep(0.1, 0.9, field); // The smear: pull the previous frame across the pitch so slices leave a // transient ghost as they land, and the corruption feels sludgy. vec3 ghost = prev(vec2(uv.x, fract(uv.y + pitch * 0.55))); col = mix(col, ghost, u_bleed * (0.4 + sliceRand)); // A thin scan beam rooting the hard edge of each step, kept local and beat // -pinned so the frame never swings in whole-frame luminance. float beamY = fract(u_beatPhase) * 2.0 - 1.0; float beam = exp(-abs(p.y - beamY * 0.6) * 6.0); col += pal(3) * beam * u_slices * 0.001 * (0.5 + u_beat); col += pal(int(mod(slice, 5.0))) * (1.0 - sliceRand) * u_glow * 0.3 * sliceRand; col += sigGrain(uv); return vec4(col, 1.0); } `, };