{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "underwater-ripple",
  "title": "Underwater Ripple",
  "description": "Hold a scene under moving water: every pixel is read through a layered refraction field that closes on a whole number of cycles, with colour fringing where the water bends hardest.",
  "dependencies": ["remotion"],
  "registryDependencies": ["@remocn/canvas-presentation"],
  "files": [
    {
      "path": "registry/remocn/underwater-ripple/index.tsx",
      "content": "\"use client\";\n\nimport type React from \"react\";\nimport { AbsoluteFill, useCurrentFrame, useVideoConfig } from \"remotion\";\nimport {\n  type CanvasFilterProps,\n  makeCanvasFilter,\n  makeFilterShader,\n} from \"@/lib/remocn/canvas-presentation\";\n\nexport type UnderwaterRippleProps = {\n  amplitude?: number;\n  scale?: number;\n  speed?: number;\n  dispersion?: number;\n};\n\nconst DEFAULTS = {\n  amplitude: 7,\n  scale: 1,\n  speed: 2,\n  dispersion: 1,\n};\n\ntype NormalizedProps = {\n  amplitude: number;\n  scale: number;\n  phase: number;\n  dispersion: number;\n};\n\nconst FRAGMENT_SHADER = `#version 300 es\nprecision highp float;\n\nuniform sampler2D u_scene;\nuniform float u_aspect;\nuniform float u_phase;\nuniform float u_amplitude;\nuniform float u_scale;\nuniform float u_dispersion;\n\nin vec2 v_uv;\nout vec4 outColor;\n\nvec2 fieldGradient(vec2 p, float phase) {\n  float scale = max(u_scale, 0.05);\n  vec2 k1 = vec2(1.0, 0.35);\n  vec2 k2 = vec2(-0.42, 1.0);\n  vec2 k3 = vec2(0.78, -0.63);\n  float f1 = 6.1 * scale;\n  float f2 = 8.7 * scale;\n  float f3 = 12.3 * scale;\n\n  vec2 g = vec2(0.0);\n  g += cos(dot(p, k1) * f1 + phase) * f1 * k1;\n  g += cos(dot(p, k2) * f2 - phase * 2.0) * f2 * k2;\n  g += cos(dot(p, k3) * f3 + phase * 3.0) * f3 * k3;\n\n  return g / (3.0 * f2);\n}\n\nvoid main() {\n  vec2 p = vec2(v_uv.x * u_aspect, v_uv.y);\n  vec2 grad = fieldGradient(p, u_phase);\n\n  vec2 offset = grad * u_amplitude;\n  float strength = length(grad);\n  vec2 direction = strength > 0.0001 ? grad / strength : vec2(0.0);\n  float spread = u_dispersion * u_amplitude * 0.3 * min(strength, 2.0);\n\n  vec2 base = v_uv + vec2(offset.x / u_aspect, offset.y);\n  vec2 lead = vec2(direction.x / u_aspect, direction.y) * spread;\n\n  vec4 center = texture(u_scene, base);\n  float r = texture(u_scene, base + lead).r;\n  float b = texture(u_scene, base - lead).b;\n\n  outColor = vec4(r, center.g, b, center.a);\n}`;\n\nconst shader = makeFilterShader<NormalizedProps>(\n  FRAGMENT_SHADER,\n  ({ gl, uniform, passedProps }) => {\n    const { amplitude, scale, phase, dispersion } = passedProps;\n    gl.uniform1f(uniform(\"u_amplitude\"), amplitude);\n    gl.uniform1f(uniform(\"u_scale\"), scale);\n    gl.uniform1f(uniform(\"u_phase\"), phase);\n    gl.uniform1f(uniform(\"u_dispersion\"), dispersion);\n  },\n);\n\nconst UnderwaterRippleFallback: React.FC<\n  NormalizedProps & CanvasFilterProps\n> = ({ children }) => <AbsoluteFill>{children}</AbsoluteFill>;\n\nconst UnderwaterRippleCanvas = makeCanvasFilter<NormalizedProps>({\n  shader,\n  fallback: UnderwaterRippleFallback,\n});\n\nexport const UnderwaterRipple: React.FC<\n  UnderwaterRippleProps & CanvasFilterProps\n> = ({\n  amplitude = DEFAULTS.amplitude,\n  scale = DEFAULTS.scale,\n  speed = DEFAULTS.speed,\n  dispersion = DEFAULTS.dispersion,\n  children,\n}) => {\n  const frame = useCurrentFrame();\n  const { height, durationInFrames } = useVideoConfig();\n  const cycle = Math.max(durationInFrames, 1);\n\n  return (\n    <UnderwaterRippleCanvas\n      amplitude={amplitude / height}\n      scale={scale}\n      phase={(frame / cycle) * speed * Math.PI * 2}\n      dispersion={dispersion}\n    >\n      {children}\n    </UnderwaterRippleCanvas>\n  );\n};\n",
      "type": "registry:component",
      "target": "components/remocn/underwater-ripple.tsx"
    }
  ],
  "type": "registry:component"
}
