{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "Dither-TS-CSS",
	"title": "Dither",
	"description": "Retro dithered noise shader background.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:file",
			"path": "Dither.css",
			"target": "@components/Dither.css",
			"content": ".dither-container {\n  width: 100%;\n  height: 100%;\n  position: relative;\n}\n"
		},
		{
			"type": "registry:component",
			"path": "Dither.tsx",
			"content": "/* eslint-disable react/no-unknown-property */\nimport { useRef, useEffect, forwardRef } from 'react';\nimport { Canvas, useFrame, useThree, type ThreeEvent } from '@react-three/fiber';\nimport { EffectComposer, wrapEffect } from '@react-three/postprocessing';\nimport { Effect } from 'postprocessing';\nimport * as THREE from 'three';\n\nimport './Dither.css';\n\nconst waveVertexShader = `\nprecision highp float;\nvarying vec2 vUv;\nvoid main() {\n  vUv = uv;\n  vec4 modelPosition = modelMatrix * vec4(position, 1.0);\n  vec4 viewPosition = viewMatrix * modelPosition;\n  gl_Position = projectionMatrix * viewPosition;\n}\n`;\n\nconst waveFragmentShader = `\nprecision highp float;\nuniform vec2 resolution;\nuniform float time;\nuniform float waveSpeed;\nuniform float waveFrequency;\nuniform float waveAmplitude;\nuniform vec3 waveColor;\nuniform vec3 backgroundColor;\nuniform vec2 mousePos;\nuniform int enableMouseInteraction;\nuniform float mouseRadius;\n\nvec4 mod289(vec4 x) { return x - floor(x * (1.0/289.0)) * 289.0; }\nvec4 permute(vec4 x) { return mod289(((x * 34.0) + 1.0) * x); }\nvec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }\nvec2 fade(vec2 t) { return t*t*t*(t*(t*6.0-15.0)+10.0); }\n\nfloat cnoise(vec2 P) {\n  vec4 Pi = floor(P.xyxy) + vec4(0.0,0.0,1.0,1.0);\n  vec4 Pf = fract(P.xyxy) - vec4(0.0,0.0,1.0,1.0);\n  Pi = mod289(Pi);\n  vec4 ix = Pi.xzxz;\n  vec4 iy = Pi.yyww;\n  vec4 fx = Pf.xzxz;\n  vec4 fy = Pf.yyww;\n  vec4 i = permute(permute(ix) + iy);\n  vec4 gx = fract(i * (1.0/41.0)) * 2.0 - 1.0;\n  vec4 gy = abs(gx) - 0.5;\n  vec4 tx = floor(gx + 0.5);\n  gx = gx - tx;\n  vec2 g00 = vec2(gx.x, gy.x);\n  vec2 g10 = vec2(gx.y, gy.y);\n  vec2 g01 = vec2(gx.z, gy.z);\n  vec2 g11 = vec2(gx.w, gy.w);\n  vec4 norm = taylorInvSqrt(vec4(dot(g00,g00), dot(g01,g01), dot(g10,g10), dot(g11,g11)));\n  g00 *= norm.x; g01 *= norm.y; g10 *= norm.z; g11 *= norm.w;\n  float n00 = dot(g00, vec2(fx.x, fy.x));\n  float n10 = dot(g10, vec2(fx.y, fy.y));\n  float n01 = dot(g01, vec2(fx.z, fy.z));\n  float n11 = dot(g11, vec2(fx.w, fy.w));\n  vec2 fade_xy = fade(Pf.xy);\n  vec2 n_x = mix(vec2(n00, n01), vec2(n10, n11), fade_xy.x);\n  return 2.3 * mix(n_x.x, n_x.y, fade_xy.y);\n}\n\nconst int OCTAVES = 4;\nfloat fbm(vec2 p) {\n  float value = 0.0;\n  float amp = 1.0;\n  float freq = waveFrequency;\n  for (int i = 0; i < OCTAVES; i++) {\n    value += amp * abs(cnoise(p));\n    p *= freq;\n    amp *= waveAmplitude;\n  }\n  return value;\n}\n\nfloat pattern(vec2 p) {\n  vec2 p2 = p - time * waveSpeed;\n  return fbm(p + fbm(p2)); \n}\n\nvoid main() {\n  vec2 uv = gl_FragCoord.xy / resolution.xy;\n  uv -= 0.5;\n  uv.x *= resolution.x / resolution.y;\n  float f = pattern(uv);\n  if (enableMouseInteraction == 1) {\n    vec2 mouseNDC = (mousePos / resolution - 0.5) * vec2(1.0, -1.0);\n    mouseNDC.x *= resolution.x / resolution.y;\n    float dist = length(uv - mouseNDC);\n    float effect = 1.0 - smoothstep(0.0, mouseRadius, dist);\n    f -= 0.5 * effect;\n  }\n  vec3 col = mix(backgroundColor, waveColor, clamp(f, 0.0, 1.0));\n  gl_FragColor = vec4(col, 1.0);\n}\n`;\n\nconst ditherFragmentShader = `\nprecision highp float;\nuniform float colorNum;\nuniform float pixelSize;\nconst float bayerMatrix8x8[64] = float[64](\n  0.0/64.0, 48.0/64.0, 12.0/64.0, 60.0/64.0,  3.0/64.0, 51.0/64.0, 15.0/64.0, 63.0/64.0,\n  32.0/64.0,16.0/64.0, 44.0/64.0, 28.0/64.0, 35.0/64.0,19.0/64.0, 47.0/64.0, 31.0/64.0,\n  8.0/64.0, 56.0/64.0,  4.0/64.0, 52.0/64.0, 11.0/64.0,59.0/64.0,  7.0/64.0, 55.0/64.0,\n  40.0/64.0,24.0/64.0, 36.0/64.0, 20.0/64.0, 43.0/64.0,27.0/64.0, 39.0/64.0, 23.0/64.0,\n  2.0/64.0, 50.0/64.0, 14.0/64.0, 62.0/64.0,  1.0/64.0,49.0/64.0, 13.0/64.0, 61.0/64.0,\n  34.0/64.0,18.0/64.0, 46.0/64.0, 30.0/64.0, 33.0/64.0,17.0/64.0, 45.0/64.0, 29.0/64.0,\n  10.0/64.0,58.0/64.0,  6.0/64.0, 54.0/64.0,  9.0/64.0,57.0/64.0,  5.0/64.0, 53.0/64.0,\n  42.0/64.0,26.0/64.0, 38.0/64.0, 22.0/64.0, 41.0/64.0,25.0/64.0, 37.0/64.0, 21.0/64.0\n);\n\nvec3 dither(vec2 uv, vec3 color) {\n  vec2 scaledCoord = floor(uv * resolution / pixelSize);\n  int x = int(mod(scaledCoord.x, 8.0));\n  int y = int(mod(scaledCoord.y, 8.0));\n  float threshold = bayerMatrix8x8[y * 8 + x] - 0.25;\n  float step = 1.0 / (colorNum - 1.0);\n  color += threshold * step;\n  float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));\n  float bias = mix(0.2, 0.0, smoothstep(0.45, 0.8, luminance));\n  color = clamp(color - bias, 0.0, 1.0);\n  return floor(color * (colorNum - 1.0) + 0.5) / (colorNum - 1.0);\n}\n\nvoid mainImage(in vec4 inputColor, in vec2 uv, out vec4 outputColor) {\n  vec2 normalizedPixelSize = pixelSize / resolution;\n  vec2 uvPixel = normalizedPixelSize * floor(uv / normalizedPixelSize);\n  vec4 color = texture2D(inputBuffer, uvPixel);\n  color.rgb = dither(uv, color.rgb);\n  outputColor = color;\n}\n`;\n\nclass RetroEffectImpl extends Effect {\n  public uniforms: Map<string, THREE.Uniform<any>>;\n  constructor() {\n    const uniforms = new Map<string, THREE.Uniform<any>>([\n      ['colorNum', new THREE.Uniform(4.0)],\n      ['pixelSize', new THREE.Uniform(2.0)]\n    ]);\n    super('RetroEffect', ditherFragmentShader, { uniforms });\n    this.uniforms = uniforms;\n  }\n  set colorNum(value: number) {\n    this.uniforms.get('colorNum')!.value = value;\n  }\n  get colorNum(): number {\n    return this.uniforms.get('colorNum')!.value;\n  }\n  set pixelSize(value: number) {\n    this.uniforms.get('pixelSize')!.value = value;\n  }\n  get pixelSize(): number {\n    return this.uniforms.get('pixelSize')!.value;\n  }\n}\n\nconst RetroEffect = forwardRef<RetroEffectImpl, { colorNum: number; pixelSize: number }>((props, ref) => {\n  const { colorNum, pixelSize } = props;\n  const WrappedRetroEffect = wrapEffect(RetroEffectImpl);\n  return <WrappedRetroEffect ref={ref} colorNum={colorNum} pixelSize={pixelSize} />;\n});\n\nRetroEffect.displayName = 'RetroEffect';\n\ninterface WaveUniforms {\n  [key: string]: THREE.Uniform<any>;\n  time: THREE.Uniform<number>;\n  resolution: THREE.Uniform<THREE.Vector2>;\n  waveSpeed: THREE.Uniform<number>;\n  waveFrequency: THREE.Uniform<number>;\n  waveAmplitude: THREE.Uniform<number>;\n  waveColor: THREE.Uniform<THREE.Color>;\n  backgroundColor: THREE.Uniform<THREE.Color>;\n  mousePos: THREE.Uniform<THREE.Vector2>;\n  enableMouseInteraction: THREE.Uniform<number>;\n  mouseRadius: THREE.Uniform<number>;\n}\n\ninterface DitheredWavesProps {\n  waveSpeed: number;\n  waveFrequency: number;\n  waveAmplitude: number;\n  waveColor: [number, number, number];\n  backgroundColor: [number, number, number];\n  colorNum: number;\n  pixelSize: number;\n  disableAnimation: boolean;\n  enableMouseInteraction: boolean;\n  mouseRadius: number;\n}\n\nfunction DitheredWaves({\n  waveSpeed,\n  waveFrequency,\n  waveAmplitude,\n  waveColor,\n  backgroundColor,\n  colorNum,\n  pixelSize,\n  disableAnimation,\n  enableMouseInteraction,\n  mouseRadius\n}: DitheredWavesProps) {\n  const mesh = useRef<THREE.Mesh>(null);\n  const mouseRef = useRef(new THREE.Vector2());\n  const { viewport, size, gl } = useThree();\n\n  const waveUniformsRef = useRef<WaveUniforms>({\n    time: new THREE.Uniform(0),\n    resolution: new THREE.Uniform(new THREE.Vector2(0, 0)),\n    waveSpeed: new THREE.Uniform(waveSpeed),\n    waveFrequency: new THREE.Uniform(waveFrequency),\n    waveAmplitude: new THREE.Uniform(waveAmplitude),\n    waveColor: new THREE.Uniform(new THREE.Color(...waveColor)),\n    backgroundColor: new THREE.Uniform(new THREE.Color(...backgroundColor)),\n    mousePos: new THREE.Uniform(new THREE.Vector2(0, 0)),\n    enableMouseInteraction: new THREE.Uniform(enableMouseInteraction ? 1 : 0),\n    mouseRadius: new THREE.Uniform(mouseRadius)\n  });\n\n  useEffect(() => {\n    const dpr = gl.getPixelRatio();\n    const newWidth = Math.floor(size.width * dpr);\n    const newHeight = Math.floor(size.height * dpr);\n    const currentRes = waveUniformsRef.current.resolution.value;\n    if (currentRes.x !== newWidth || currentRes.y !== newHeight) {\n      currentRes.set(newWidth, newHeight);\n    }\n  }, [size, gl]);\n\n  const prevColor = useRef([...waveColor]);\n  const prevBackgroundColor = useRef([...backgroundColor]);\n  useFrame(({ clock }) => {\n    const u = waveUniformsRef.current;\n\n    if (!disableAnimation) {\n      u.time.value = clock.getElapsedTime();\n    }\n\n    if (u.waveSpeed.value !== waveSpeed) u.waveSpeed.value = waveSpeed;\n    if (u.waveFrequency.value !== waveFrequency) u.waveFrequency.value = waveFrequency;\n    if (u.waveAmplitude.value !== waveAmplitude) u.waveAmplitude.value = waveAmplitude;\n\n    if (!prevColor.current.every((v, i) => v === waveColor[i])) {\n      u.waveColor.value.set(...waveColor);\n      prevColor.current = [...waveColor];\n    }\n\n    if (!prevBackgroundColor.current.every((v, i) => v === backgroundColor[i])) {\n      u.backgroundColor.value.set(...backgroundColor);\n      prevBackgroundColor.current = [...backgroundColor];\n    }\n\n    u.enableMouseInteraction.value = enableMouseInteraction ? 1 : 0;\n    u.mouseRadius.value = mouseRadius;\n\n    if (enableMouseInteraction) {\n      u.mousePos.value.copy(mouseRef.current);\n    }\n  });\n\n  const handlePointerMove = (e: ThreeEvent<PointerEvent>) => {\n    if (!enableMouseInteraction) return;\n    const rect = gl.domElement.getBoundingClientRect();\n    const dpr = gl.getPixelRatio();\n    mouseRef.current.set((e.clientX - rect.left) * dpr, (e.clientY - rect.top) * dpr);\n  };\n\n  return (\n    <>\n      <mesh ref={mesh} scale={[viewport.width, viewport.height, 1]}>\n        <planeGeometry args={[1, 1]} />\n        <shaderMaterial\n          vertexShader={waveVertexShader}\n          fragmentShader={waveFragmentShader}\n          uniforms={waveUniformsRef.current}\n        />\n      </mesh>\n\n      <EffectComposer>\n        <RetroEffect colorNum={colorNum} pixelSize={pixelSize} />\n      </EffectComposer>\n\n      <mesh\n        onPointerMove={handlePointerMove}\n        position={[0, 0, 0.01]}\n        scale={[viewport.width, viewport.height, 1]}\n        visible={false}\n      >\n        <planeGeometry args={[1, 1]} />\n        <meshBasicMaterial transparent opacity={0} />\n      </mesh>\n    </>\n  );\n}\n\ninterface DitherProps {\n  waveSpeed?: number;\n  waveFrequency?: number;\n  waveAmplitude?: number;\n  waveColor?: [number, number, number];\n  backgroundColor?: [number, number, number];\n  colorNum?: number;\n  pixelSize?: number;\n  disableAnimation?: boolean;\n  enableMouseInteraction?: boolean;\n  mouseRadius?: number;\n}\n\nexport default function Dither({\n  waveSpeed = 0.05,\n  waveFrequency = 3,\n  waveAmplitude = 0.3,\n  waveColor = [0.5, 0.5, 0.5],\n  backgroundColor = [0, 0, 0],\n  colorNum = 4,\n  pixelSize = 2,\n  disableAnimation = false,\n  enableMouseInteraction = true,\n  mouseRadius = 1\n}: DitherProps) {\n  return (\n    <Canvas\n      className=\"dither-container\"\n      camera={{ position: [0, 0, 6] }}\n      dpr={1}\n      gl={{ antialias: true, preserveDrawingBuffer: true }}\n    >\n      <DitheredWaves\n        waveSpeed={waveSpeed}\n        waveFrequency={waveFrequency}\n        waveAmplitude={waveAmplitude}\n        waveColor={waveColor}\n        backgroundColor={backgroundColor}\n        colorNum={colorNum}\n        pixelSize={pixelSize}\n        disableAnimation={disableAnimation}\n        enableMouseInteraction={enableMouseInteraction}\n        mouseRadius={mouseRadius}\n      />\n    </Canvas>\n  );\n}\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"@react-three/fiber@^9.3.0",
		"@react-three/postprocessing@^3.0.4",
		"postprocessing@^6.36.0",
		"three@^0.180.0"
	]
}