{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "sustained-glitch",
  "title": "Sustained Glitch",
  "description": "Broadcast corruption held over a scene in scheduled bursts: slices tear sideways, channels split, and blocks fill with picture pulled from somewhere else — then the frame rests clean until the next hit.",
  "dependencies": ["remotion"],
  "registryDependencies": ["@remocn/canvas-presentation"],
  "files": [
    {
      "path": "registry/remocn/sustained-glitch/index.tsx",
      "content": "\"use client\";\n\nimport type React from \"react\";\nimport { AbsoluteFill, random, useCurrentFrame } from \"remotion\";\nimport {\n  type CanvasFilterProps,\n  makeCanvasFilter,\n  makeFilterShader,\n} from \"@/lib/remocn/canvas-presentation\";\n\nexport type SustainedGlitchProps = {\n  intensity?: number;\n  frequency?: number;\n  slices?: number;\n  seed?: number;\n};\n\nconst DEFAULTS = {\n  intensity: 1,\n  frequency: 1,\n  slices: 24,\n  seed: 1,\n};\n\nconst FRAGMENT_SHADER = `#version 300 es\nprecision highp float;\n\nuniform sampler2D u_scene;\nuniform float u_frame;\nuniform float u_intensity;\nuniform float u_frequency;\nuniform float u_slices;\nuniform float u_seed;\n\nin vec2 v_uv;\nout vec4 outColor;\n\nconst float BLOCK_COLUMNS = 16.0;\n\nfloat hash(vec2 p) {\n  return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);\n}\n\nvec4 sampleSplit(vec2 uv, float split) {\n  vec4 center = texture(u_scene, uv);\n  float r = texture(u_scene, fract(vec2(uv.x + split, uv.y))).r;\n  float b = texture(u_scene, fract(vec2(uv.x - split, uv.y))).b;\n  return vec4(r, center.g, b, center.a);\n}\n\nvoid main() {\n  float slotLength = max(30.0 / max(u_frequency, 0.1), 4.0);\n  float slot = floor(u_frame / slotLength);\n  float local = u_frame - slot * slotLength;\n\n  float burstLength = 2.0 + floor(hash(vec2(slot, u_seed + 2.0)) * 4.0);\n  float start =\n    floor(hash(vec2(slot, u_seed + 1.0)) * max(slotLength - burstLength, 1.0));\n  float fires = step(0.25, hash(vec2(slot, u_seed + 3.0)));\n\n  float age = local - start;\n  float live = fires * step(0.0, age) * step(age, burstLength - 1.0);\n  float energy =\n    max(u_intensity, 0.0) * live * (1.0 - age / burstLength);\n\n  if (energy <= 0.0) {\n    outColor = texture(u_scene, v_uv);\n    return;\n  }\n\n  float clock = mod(u_frame, 512.0) + u_seed;\n  float row = floor(v_uv.y * max(u_slices, 2.0));\n  float torn = step(0.45, hash(vec2(row, clock)));\n  float amount = hash(vec2(row + 17.0, clock * 1.7 + 3.0)) - 0.5;\n  float sliceShift = amount * 0.28 * energy * torn;\n\n  vec2 block = vec2(floor(v_uv.x * BLOCK_COLUMNS), row);\n  float corrupt = step(1.0 - 0.45 * min(energy, 1.0), hash(block + clock * 7.3));\n  vec2 jump =\n    vec2(\n      hash(block + clock * 3.1 + 5.0) - 0.5,\n      hash(block + clock * 9.7 + 11.0) - 0.5\n    ) *\n    vec2(0.62, 0.34) *\n    corrupt;\n\n  vec2 uv = fract(v_uv + vec2(sliceShift, 0.0) + jump);\n  float split = 0.02 * energy * (0.35 + abs(amount));\n\n  vec4 color = sampleSplit(uv, split);\n  color.rgb +=\n    vec3(0.05, 0.015, 0.09) *\n    energy *\n    torn *\n    hash(vec2(row, clock + 41.0)) *\n    color.a;\n\n  outColor = vec4(clamp(color.rgb, 0.0, 1.0), color.a);\n}`;\n\nconst shader = makeFilterShader<SustainedGlitchProps>(\n  FRAGMENT_SHADER,\n  ({ gl, uniform, passedProps }) => {\n    const {\n      intensity = DEFAULTS.intensity,\n      frequency = DEFAULTS.frequency,\n      slices = DEFAULTS.slices,\n      seed = DEFAULTS.seed,\n    } = passedProps;\n    gl.uniform1f(uniform(\"u_intensity\"), intensity);\n    gl.uniform1f(uniform(\"u_frequency\"), frequency);\n    gl.uniform1f(uniform(\"u_slices\"), slices);\n    gl.uniform1f(uniform(\"u_seed\"), seed);\n  },\n);\n\nfunction burstEnergy(\n  frame: number,\n  intensity: number,\n  frequency: number,\n  seed: number,\n): number {\n  const slotLength = Math.max(30 / Math.max(frequency, 0.1), 4);\n  const slot = Math.floor(frame / slotLength);\n  const local = frame - slot * slotLength;\n\n  const burstLength = 2 + Math.floor(random(`glitch-len-${seed}-${slot}`) * 4);\n  const start = Math.floor(\n    random(`glitch-start-${seed}-${slot}`) *\n      Math.max(slotLength - burstLength, 1),\n  );\n  if (random(`glitch-fire-${seed}-${slot}`) < 0.25) return 0;\n\n  const age = local - start;\n  if (age < 0 || age > burstLength - 1) return 0;\n  return Math.max(intensity, 0) * (1 - age / burstLength);\n}\n\nconst SustainedGlitchFallback: React.FC<\n  SustainedGlitchProps & CanvasFilterProps\n> = ({\n  children,\n  intensity = DEFAULTS.intensity,\n  frequency = DEFAULTS.frequency,\n  slices = DEFAULTS.slices,\n  seed = DEFAULTS.seed,\n}) => {\n  const frame = useCurrentFrame();\n  const energy = burstEnergy(frame, intensity, frequency, seed);\n\n  if (energy <= 0) {\n    return <AbsoluteFill>{children}</AbsoluteFill>;\n  }\n\n  const bands = Math.max(2, Math.min(6, Math.round(slices / 5)));\n  const height = 100 / bands;\n\n  return (\n    <AbsoluteFill>\n      {Array.from({ length: bands }, (_, band) => {\n        const shift =\n          (random(`glitch-shift-${seed}-${frame}-${band}`) - 0.5) * 46 * energy;\n        const tint = band % 2 === 0 ? \"rgb(255, 42, 90)\" : \"rgb(0, 224, 255)\";\n        return (\n          <AbsoluteFill\n            key={band}\n            style={{\n              clipPath: `inset(${band * height}% 0 ${100 - (band + 1) * height}% 0)`,\n              translate: `${shift}px`,\n            }}\n          >\n            {children}\n            <AbsoluteFill\n              style={{\n                background: tint,\n                mixBlendMode: \"screen\",\n                opacity:\n                  0.18 *\n                  energy *\n                  random(`glitch-tint-${seed}-${frame}-${band}`),\n              }}\n            />\n          </AbsoluteFill>\n        );\n      })}\n    </AbsoluteFill>\n  );\n};\n\nexport const SustainedGlitch = makeCanvasFilter<SustainedGlitchProps>({\n  shader,\n  fallback: SustainedGlitchFallback,\n});\n",
      "type": "registry:component",
      "target": "components/remocn/sustained-glitch.tsx"
    }
  ],
  "type": "registry:component"
}
