{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "shader-surface",
  "title": "Shader Surface",
  "description": "Four shader backdrops, each taking its two colours from your theme.",
  "registryDependencies": [
    "utils",
    "https://ui.tinkererslabs.com/r/render-surface.json"
  ],
  "files": [
    {
      "path": "registry/default/shader-surface/shader-surface.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n  RenderSurface,\n  createQuadProgram,\n  useThemeColors,\n  type QuadProgram,\n  type SurfaceColor,\n} from \"@/registry/default/render-surface/render-surface\"\nimport { cn } from \"@/lib/utils\"\n\nexport type ShaderSurfaceVariant = \"caustics\" | \"metal\" | \"plasma\" | \"ripple\"\n\nexport type ShaderSurfaceProps = React.HTMLAttributes<HTMLDivElement> & {\n  variant?: ShaderSurfaceVariant\n  /** The colour the surface settles to. A theme property or a CSS colour. */\n  base?: string\n  /** The colour the light in it takes. */\n  tint?: string\n  speed?: number\n  /** Size of the pattern. Larger is busier. */\n  scale?: number\n  paused?: boolean\n  surfaceClassName?: string\n}\n\nconst PRELUDE = `\nprecision highp float;\nvarying vec2 vUv;\nuniform vec2 uResolution;\nuniform float uTime;\nuniform float uScale;\nuniform vec3 uBase;\nuniform vec3 uTint;\n\nfloat hash(vec2 p) {\n  return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);\n}\n\nfloat noise(vec2 p) {\n  vec2 i = floor(p);\n  vec2 f = fract(p);\n  vec2 u = f * f * (3.0 - 2.0 * f);\n  return mix(\n    mix(hash(i), hash(i + vec2(1.0, 0.0)), u.x),\n    mix(hash(i + vec2(0.0, 1.0)), hash(i + vec2(1.0, 1.0)), u.x),\n    u.y\n  );\n}\n\nfloat fbm(vec2 p) {\n  float value = 0.0;\n  float amplitude = 0.5;\n  for (int i = 0; i < 5; i++) {\n    value += amplitude * noise(p);\n    p *= 2.03;\n    amplitude *= 0.5;\n  }\n  return value;\n}\n\nvec2 aspectUv() {\n  float aspect = uResolution.x / max(uResolution.y, 1.0);\n  return (vUv - 0.5) * vec2(aspect, 1.0);\n}\n`\n\nconst BODIES: Record<ShaderSurfaceVariant, string> = {\n  caustics: `\nvec3 shade() {\n  vec2 p = aspectUv() * uScale;\n  float t = uTime * 0.35;\n  vec2 q = vec2(fbm(p + t), fbm(p + vec2(5.2, 1.3) - t));\n  float f = fbm(p + 4.0 * q);\n  float light = pow(abs(sin(f * 6.2831 + t * 2.0)), 3.0);\n  return mix(uBase, uTint, light);\n}\n`,\n  metal: `\nvec3 shade() {\n  vec2 p = aspectUv() * uScale;\n  float f = fbm(p * 1.2 + vec2(uTime * 0.15, uTime * 0.05));\n  float bands = sin((p.x * 1.4 + f * 3.4) * 6.2831);\n  float shine = pow(abs(bands), 8.0);\n  vec3 body = mix(uBase, uTint, 0.3 + 0.45 * f);\n  return body + shine * 0.55;\n}\n`,\n  plasma: `\nvec3 shade() {\n  vec2 p = aspectUv() * uScale;\n  vec2 drift = vec2(sin(uTime * 0.2), cos(uTime * 0.17)) * 1.5;\n  float f = fbm(p + drift);\n  return mix(uBase, uTint, smoothstep(0.25, 0.75, f));\n}\n`,\n  ripple: `\nvec3 shade() {\n  vec2 p = aspectUv();\n  float d = length(p);\n  float wave = sin(d * uScale * 4.0 - uTime * 2.0) * 0.5 + 0.5;\n  float fade = smoothstep(1.0, 0.0, d);\n  return mix(uBase, uTint, wave * fade);\n}\n`,\n}\n\nconst MAIN = `\nvoid main() {\n  gl_FragColor = vec4(clamp(shade(), 0.0, 1.0), 1.0);\n}\n`\n\ntype Scene = { program: QuadProgram | null }\n\n/**\n * A shader-drawn background. Every variant reads its two colours from the\n * theme, so the same surface arrives dark in a dark application and light in a\n * light one without being told which it is in.\n */\nexport function ShaderSurface({\n  variant = \"plasma\",\n  base = \"--background\",\n  tint = \"--primary\",\n  speed = 1,\n  scale = 3,\n  paused,\n  className,\n  surfaceClassName,\n  children,\n  ...rootProps\n}: ShaderSurfaceProps) {\n  const rootRef = React.useRef<HTMLDivElement>(null)\n\n  const tokens = React.useMemo(\n    () => [base, tint].filter((entry) => entry.startsWith(\"--\")),\n    [base, tint]\n  )\n\n  const resolved = useThemeColors(rootRef, tokens)\n  const baseColor = base.startsWith(\"--\") ? resolved[base] : undefined\n  const tintColor = tint.startsWith(\"--\") ? resolved[tint] : undefined\n  const ready = baseColor !== undefined && tintColor !== undefined\n\n  const colorsRef = React.useRef<{ base: SurfaceColor; tint: SurfaceColor }>({\n    base: [0, 0, 0],\n    tint: [1, 1, 1],\n  })\n\n  React.useEffect(() => {\n    if (baseColor && tintColor) {\n      colorsRef.current = { base: baseColor, tint: tintColor }\n    }\n  }, [baseColor, tintColor])\n\n  const setup = React.useCallback(\n    ({ context }: { context: WebGLRenderingContext }) => ({\n      program: createQuadProgram(context, PRELUDE + BODIES[variant] + MAIN),\n    }),\n    [variant]\n  )\n\n  const draw = React.useCallback(\n    ({\n      context,\n      size,\n      state,\n      time,\n    }: {\n      context: WebGLRenderingContext\n      size: { width: number; height: number; dpr: number }\n      state: Scene\n      time: number\n    }) => {\n      const quad = state.program\n      if (!quad) return\n\n      const { width, height, dpr } = size\n      context.useProgram(quad.program)\n      context.uniform2f(quad.uniform(\"uResolution\"), width * dpr, height * dpr)\n      context.uniform1f(quad.uniform(\"uTime\"), time * speed)\n      context.uniform1f(quad.uniform(\"uScale\"), scale)\n      context.uniform3fv(quad.uniform(\"uBase\"), colorsRef.current.base)\n      context.uniform3fv(quad.uniform(\"uTint\"), colorsRef.current.tint)\n\n      quad.paint(width, height, dpr)\n    },\n    [scale, speed]\n  )\n\n  const teardown = React.useCallback((state: Scene) => {\n    state.program?.dispose()\n  }, [])\n\n  return (\n    <div\n      ref={rootRef}\n      data-slot=\"shader-surface\"\n      data-variant={variant}\n      className={cn(\n        \"bg-background relative isolate overflow-hidden\",\n        className\n      )}\n      {...rootProps}\n    >\n      {ready && (\n        <RenderSurface<Scene, \"webgl\">\n          contextType=\"webgl\"\n          setup={setup}\n          rebuildKey={variant}\n          draw={draw}\n          teardown={teardown}\n          paused={paused}\n          className={cn(\n            \"pointer-events-none absolute inset-0 -z-10\",\n            surfaceClassName\n          )}\n        />\n      )}\n      {children}\n    </div>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "scene",
    "background",
    "shader",
    "webgl"
  ],
  "type": "registry:ui"
}