{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "crt-screen",
  "title": "CRT Screen",
  "description": "Put a scene behind curved glass: the picture bulges out to the corners, an RGB phosphor mask and scanlines ride the curve, bright areas flood the mask, and the tube falls away into darkness at the rim.",
  "dependencies": ["remotion"],
  "registryDependencies": ["@remocn/canvas-presentation"],
  "files": [
    {
      "path": "registry/remocn/crt-screen/index.tsx",
      "content": "\"use client\";\n\nimport type React from \"react\";\nimport { AbsoluteFill } from \"remotion\";\nimport {\n  type CanvasFilterProps,\n  makeCanvasFilter,\n  makeFilterShader,\n} from \"@/lib/remocn/canvas-presentation\";\n\nexport type CrtScreenProps = {\n  curvature?: number;\n  scanlines?: number;\n  maskScale?: number;\n  vignette?: number;\n};\n\nconst DEFAULTS = {\n  curvature: 1,\n  scanlines: 1,\n  maskScale: 2,\n  vignette: 1,\n};\n\nconst FRAGMENT_SHADER = `#version 300 es\nprecision highp float;\n\nuniform sampler2D u_scene;\nuniform float u_frame;\nuniform float u_curvature;\nuniform float u_scanlines;\nuniform float u_mask_scale;\nuniform float u_vignette;\n\nin vec2 v_uv;\nout vec4 outColor;\n\nconst float PI = 3.141592653589793;\nconst float SCAN_LINES = 240.0;\n\nvec2 warpTube(vec2 uv, float k) {\n  vec2 p = uv * 2.0 - 1.0;\n  float x2 = p.x * p.x;\n  float y2 = p.y * p.y;\n  p.x *= 1.0 + k * 0.11 * y2;\n  p.y *= 1.0 + k * 0.17 * x2;\n  p /= 1.0 + k * 0.045;\n  return p * 0.5 + 0.5;\n}\n\nvec3 phosphorMask(float x, float scale) {\n  float sub = mod(floor(x / max(scale, 1.0)), 3.0);\n  vec3 lit = vec3(\n    step(sub, 0.5),\n    step(0.5, sub) * step(sub, 1.5),\n    step(1.5, sub)\n  );\n  return mix(vec3(0.34), vec3(1.0), lit);\n}\n\nvoid main() {\n  vec2 uv = warpTube(v_uv, u_curvature);\n\n  vec2 edge =\n    smoothstep(vec2(0.0), vec2(0.005), uv) *\n    smoothstep(vec2(0.0), vec2(0.005), 1.0 - uv);\n  float inside = edge.x * edge.y;\n\n  vec4 base = texture(u_scene, uv);\n\n  vec3 spread = vec3(0.0);\n  for (int i = -2; i <= 2; i++) {\n    spread += texture(u_scene, uv + vec2(float(i) * 0.0018, 0.0)).rgb;\n  }\n  spread /= 5.0;\n  vec3 bloom = max(spread - 0.5, vec3(0.0));\n\n  vec3 color = base.rgb;\n  color *= phosphorMask(gl_FragCoord.x, u_mask_scale);\n  color += bloom * 0.75;\n\n  float scan = mix(1.0, 0.5 + 0.5 * cos(uv.y * SCAN_LINES * PI * 2.0), 0.55);\n  color *= mix(1.0, scan, clamp(u_scanlines, 0.0, 1.5));\n\n  vec2 vc = uv * 2.0 - 1.0;\n  float radius = length(vc * vec2(0.86, 1.0));\n  color *= mix(1.0, smoothstep(1.6, 0.35, radius), clamp(u_vignette, 0.0, 1.5));\n\n  color *= 1.0 + 0.012 * sin(u_frame * PI / 30.0);\n  color *= inside;\n\n  outColor = vec4(clamp(color, 0.0, 1.0), max(base.a, 1.0 - inside));\n}`;\n\nconst shader = makeFilterShader<CrtScreenProps>(\n  FRAGMENT_SHADER,\n  ({ gl, uniform, passedProps }) => {\n    const {\n      curvature = DEFAULTS.curvature,\n      scanlines = DEFAULTS.scanlines,\n      maskScale = DEFAULTS.maskScale,\n      vignette = DEFAULTS.vignette,\n    } = passedProps;\n    gl.uniform1f(uniform(\"u_curvature\"), curvature);\n    gl.uniform1f(uniform(\"u_scanlines\"), scanlines);\n    gl.uniform1f(uniform(\"u_mask_scale\"), maskScale);\n    gl.uniform1f(uniform(\"u_vignette\"), vignette);\n  },\n);\n\nconst CrtScreenFallback: React.FC<CrtScreenProps & CanvasFilterProps> = ({\n  children,\n  scanlines = DEFAULTS.scanlines,\n  vignette = DEFAULTS.vignette,\n}) => (\n  <AbsoluteFill>\n    <AbsoluteFill style={{ filter: \"saturate(1.1) contrast(1.06)\" }}>\n      {children}\n    </AbsoluteFill>\n    <AbsoluteFill\n      style={{\n        background:\n          \"repeating-linear-gradient(to bottom, rgba(0, 0, 0, 0.34) 0px, rgba(0, 0, 0, 0.34) 1.5px, transparent 1.5px, transparent 3px)\",\n        opacity: Math.min(scanlines, 1),\n      }}\n    />\n    <AbsoluteFill\n      style={{\n        background:\n          \"radial-gradient(ellipse 82% 78% at 50% 50%, transparent 45%, rgba(0, 0, 0, 0.72) 100%)\",\n        opacity: Math.min(vignette, 1),\n      }}\n    />\n  </AbsoluteFill>\n);\n\nexport const CrtScreen = makeCanvasFilter<CrtScreenProps>({\n  shader,\n  fallback: CrtScreenFallback,\n});\n",
      "type": "registry:component",
      "target": "components/remocn/crt-screen.tsx"
    }
  ],
  "type": "registry:component"
}
