{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "security-cam",
  "title": "Security Cam",
  "description": "Run a scene through a cheap recorder: macroblocks flatten under a starved bitrate, colour resolution collapses to half the luma, blacks crush and shadows crawl with sensor noise.",
  "dependencies": ["remotion"],
  "registryDependencies": ["@remocn/canvas-presentation"],
  "files": [
    {
      "path": "registry/remocn/security-cam/index.tsx",
      "content": "\"use client\";\n\nimport type React from \"react\";\nimport { AbsoluteFill, useVideoConfig } from \"remotion\";\nimport {\n  type CanvasFilterProps,\n  makeCanvasFilter,\n  makeFilterShader,\n} from \"@/lib/remocn/canvas-presentation\";\n\nexport type SecurityCamProps = {\n  compression?: number;\n  blockSize?: number;\n  noise?: number;\n  intensity?: number;\n};\n\nconst DEFAULTS = {\n  compression: 0.7,\n  blockSize: 10,\n  noise: 0.6,\n  intensity: 1,\n};\n\ntype NormalizedProps = {\n  compression: number;\n  cellX: number;\n  cellY: number;\n  noise: number;\n  intensity: number;\n};\n\nconst FRAGMENT_SHADER = `#version 300 es\nprecision highp float;\n\nuniform sampler2D u_scene;\nuniform float u_frame;\nuniform vec2 u_cell;\nuniform float u_compression;\nuniform float u_noise;\nuniform float u_intensity;\n\nin vec2 v_uv;\nout vec4 outColor;\n\nconst float PI = 3.141592653589793;\n\nfloat hash(vec2 p) {\n  return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453123);\n}\n\nfloat lumaOf(vec3 c) {\n  return dot(c, vec3(0.299, 0.587, 0.114));\n}\n\nvec2 chromaOf(vec3 c) {\n  return vec2(c.b - lumaOf(c), c.r - lumaOf(c));\n}\n\nvec3 fromLumaChroma(float y, vec2 cbcr) {\n  return vec3(y + cbcr.y, y - 0.194 * cbcr.x - 0.509 * cbcr.y, y + cbcr.x);\n}\n\nvec3 blockMean(vec2 origin, vec2 cell) {\n  vec3 total = vec3(0.0);\n  for (int y = 0; y < 2; y++) {\n    for (int x = 0; x < 2; x++) {\n      vec2 at = origin + (vec2(float(x), float(y)) + 0.5) * cell * 0.5;\n      total += texture(u_scene, at).rgb;\n    }\n  }\n  return total * 0.25;\n}\n\nvoid main() {\n  vec4 src = texture(u_scene, v_uv);\n  float amount = clamp(u_intensity, 0.0, 1.0);\n\n  if (!(amount > 0.001)) {\n    outColor = src;\n    return;\n  }\n\n  vec2 cell = max(u_cell, vec2(0.0008));\n  vec2 wide = cell * 2.0;\n\n  vec3 lumaBlock = blockMean(floor(v_uv / cell) * cell, cell);\n  vec3 chromaBlock = blockMean(floor(v_uv / wide) * wide, wide);\n\n  float squeeze = clamp(u_compression, 0.0, 1.0) * amount;\n\n  float y = mix(lumaOf(src.rgb), lumaOf(lumaBlock), squeeze * 0.8);\n  float levels = mix(255.0, 20.0, squeeze);\n  y = floor(y * levels + 0.5) / levels;\n\n  vec2 cbcr = mix(chromaOf(src.rgb), chromaOf(chromaBlock), squeeze);\n  cbcr *= mix(1.0, 0.62, amount);\n\n  vec3 color = fromLumaChroma(y, cbcr);\n\n  float pump = 1.0 + 0.03 * sin(u_frame * PI / 45.0) * amount;\n  color = clamp((color * pump - 0.05 * amount) / (1.0 - 0.14 * amount), 0.0, 1.0);\n\n  float clock = mod(u_frame, 64.0);\n  float shadow = 1.0 - smoothstep(0.0, 0.55, y);\n  float grain =\n    hash(v_uv + vec2(clock * 0.017, clock * 0.029)) - 0.5;\n  color += grain * 0.16 * u_noise * amount * (0.25 + shadow);\n\n  outColor = vec4(clamp(color, 0.0, 1.0), src.a);\n}`;\n\nconst shader = makeFilterShader<NormalizedProps>(\n  FRAGMENT_SHADER,\n  ({ gl, uniform, passedProps }) => {\n    const { compression, cellX, cellY, noise, intensity } = passedProps;\n    gl.uniform1f(uniform(\"u_compression\"), compression);\n    gl.uniform2f(uniform(\"u_cell\"), cellX, cellY);\n    gl.uniform1f(uniform(\"u_noise\"), noise);\n    gl.uniform1f(uniform(\"u_intensity\"), intensity);\n  },\n);\n\nconst SecurityCamFallback: React.FC<NormalizedProps & CanvasFilterProps> = ({\n  children,\n  intensity,\n}) => (\n  <AbsoluteFill>\n    <AbsoluteFill\n      style={{\n        filter: `saturate(${1 - 0.38 * intensity}) contrast(${1 + 0.22 * intensity}) brightness(${1 - 0.05 * intensity})`,\n      }}\n    >\n      {children}\n    </AbsoluteFill>\n    <AbsoluteFill\n      style={{\n        background:\n          \"radial-gradient(ellipse 82% 80% at 50% 50%, transparent 50%, rgba(0, 0, 0, 0.55) 100%)\",\n        opacity: intensity,\n      }}\n    />\n  </AbsoluteFill>\n);\n\nconst SecurityCamCanvas = makeCanvasFilter<NormalizedProps>({\n  shader,\n  fallback: SecurityCamFallback,\n});\n\nexport const SecurityCam: React.FC<SecurityCamProps & CanvasFilterProps> = ({\n  compression = DEFAULTS.compression,\n  blockSize = DEFAULTS.blockSize,\n  noise = DEFAULTS.noise,\n  intensity = DEFAULTS.intensity,\n  children,\n}) => {\n  const { width, height } = useVideoConfig();\n\n  return (\n    <SecurityCamCanvas\n      compression={compression}\n      cellX={Math.max(blockSize, 1) / width}\n      cellY={Math.max(blockSize, 1) / height}\n      noise={noise}\n      intensity={intensity}\n    >\n      {children}\n    </SecurityCamCanvas>\n  );\n};\n",
      "type": "registry:component",
      "target": "components/remocn/security-cam.tsx"
    }
  ],
  "type": "registry:component"
}
