{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pixel-snow",
  "type": "registry:block",
  "title": "Pixel snow",
  "description": "Pixel snow",
  "files": [
    {
      "path": "components/usages/pixelsnowusage.tsx",
      "content": "\"use client\";\r\n\r\nimport PixelSnow from \"@/registry/open-source/pixel-snow\";\r\nimport React from \"react\";\r\n\r\n\r\nexport default function Usage() {\r\n    return (\r\n        <div className=\"h-screen w-full flex items-center justify-center relative overflow-hidden bg-background\">\r\n            <PixelSnow />\r\n        </div>\r\n    );\r\n}\r\n",
      "type": "registry:block",
      "target": "~/example.tsx"
    },
    {
      "path": "components/usages/pixelsnowusage.tsx",
      "content": "\"use client\";\r\n\r\nimport PixelSnow from \"@/registry/open-source/pixel-snow\";\r\nimport React from \"react\";\r\n\r\n\r\nexport default function Usage() {\r\n    return (\r\n        <div className=\"h-screen w-full flex items-center justify-center relative overflow-hidden bg-background\">\r\n            <PixelSnow />\r\n        </div>\r\n    );\r\n}\r\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/pixel-snow.tsx",
      "content": "import { useEffect, useRef, useMemo, useCallback } from 'react';\nimport {\n    Scene,\n    OrthographicCamera,\n    WebGLRenderer,\n    PlaneGeometry,\n    ShaderMaterial,\n    Mesh,\n    Vector2,\n    Vector3,\n    Color\n} from 'three';\n\nconst vertexShader = `\nvoid main() {\n  gl_Position = vec4(position, 1.0);\n}\n`;\n\nconst fragmentShader = `\nprecision mediump float;\n\nuniform float uTime;\nuniform vec2 uResolution;\nuniform float uFlakeSize;\nuniform float uMinFlakeSize;\nuniform float uPixelResolution;\nuniform float uSpeed;\nuniform float uDepthFade;\nuniform float uFarPlane;\nuniform vec3 uColor;\nuniform float uBrightness;\nuniform float uGamma;\nuniform float uDensity;\nuniform float uVariant;\nuniform float uDirection;\n\n// Precomputed constants\n#define PI 3.14159265\n#define PI_OVER_6 0.5235988\n#define PI_OVER_3 1.0471976\n#define INV_SQRT3 0.57735027\n#define M1 1597334677U\n#define M2 3812015801U\n#define M3 3299493293U\n#define F0 2.3283064e-10\n\n// Optimized hash - inline multiplication\n#define hash(n) (n * (n ^ (n >> 15)))\n#define coord3(p) (uvec3(p).x * M1 ^ uvec3(p).y * M2 ^ uvec3(p).z * M3)\n\n// Precomputed camera basis vectors (normalized vec3(1,1,1), vec3(1,0,-1))\nconst vec3 camK = vec3(0.57735027, 0.57735027, 0.57735027);\nconst vec3 camI = vec3(0.70710678, 0.0, -0.70710678);\nconst vec3 camJ = vec3(-0.40824829, 0.81649658, -0.40824829);\n\n// Precomputed branch direction\nconst vec2 b1d = vec2(0.574, 0.819);\n\nvec3 hash3(uint n) {\n  uvec3 hashed = hash(n) * uvec3(1U, 511U, 262143U);\n  return vec3(hashed) * F0;\n}\n\nfloat snowflakeDist(vec2 p) {\n  float r = length(p);\n  float a = atan(p.y, p.x);\n  a = abs(mod(a + PI_OVER_6, PI_OVER_3) - PI_OVER_6);\n  vec2 q = r * vec2(cos(a), sin(a));\n  float dMain = max(abs(q.y), max(-q.x, q.x - 1.0));\n  float b1t = clamp(dot(q - vec2(0.4, 0.0), b1d), 0.0, 0.4);\n  float dB1 = length(q - vec2(0.4, 0.0) - b1t * b1d);\n  float b2t = clamp(dot(q - vec2(0.7, 0.0), b1d), 0.0, 0.25);\n  float dB2 = length(q - vec2(0.7, 0.0) - b2t * b1d);\n  return min(dMain, min(dB1, dB2)) * 10.0;\n}\n\nvoid main() {\n  // Precompute reciprocals to avoid division\n  float invPixelRes = 1.0 / uPixelResolution;\n  float pixelSize = max(1.0, floor(0.5 + uResolution.x * invPixelRes));\n  float invPixelSize = 1.0 / pixelSize;\n  \n  vec2 fragCoord = floor(gl_FragCoord.xy * invPixelSize);\n  vec2 res = uResolution * invPixelSize;\n  float invResX = 1.0 / res.x;\n\n  vec3 ray = normalize(vec3((fragCoord - res * 0.5) * invResX, 1.0));\n  ray = ray.x * camI + ray.y * camJ + ray.z * camK;\n\n  // Precompute time-based values\n  float timeSpeed = uTime * uSpeed;\n  float windX = cos(uDirection) * 0.4;\n  float windY = sin(uDirection) * 0.4;\n  vec3 camPos = (windX * camI + windY * camJ + 0.1 * camK) * timeSpeed;\n  vec3 pos = camPos;\n\n  // Precompute ray reciprocal for strides\n  vec3 absRay = max(abs(ray), vec3(0.001));\n  vec3 strides = 1.0 / absRay;\n  vec3 raySign = step(ray, vec3(0.0));\n  vec3 phase = fract(pos) * strides;\n  phase = mix(strides - phase, phase, raySign);\n\n  // Precompute for intersection test\n  float rayDotCamK = dot(ray, camK);\n  float invRayDotCamK = 1.0 / rayDotCamK;\n  float invDepthFade = 1.0 / uDepthFade;\n  float halfInvResX = 0.5 * invResX;\n  vec3 timeAnim = timeSpeed * 0.1 * vec3(7.0, 8.0, 5.0);\n\n  float t = 0.0;\n  for (int i = 0; i < 128; i++) {\n    if (t >= uFarPlane) break;\n    \n    vec3 fpos = floor(pos);\n    uint cellCoord = coord3(fpos);\n    float cellHash = hash3(cellCoord).x;\n\n    if (cellHash < uDensity) {\n      vec3 h = hash3(cellCoord);\n      \n      // Optimized flake position calculation\n      vec3 sinArg1 = fpos.yzx * 0.073;\n      vec3 sinArg2 = fpos.zxy * 0.27;\n      vec3 flakePos = 0.5 - 0.5 * cos(4.0 * sin(sinArg1) + 4.0 * sin(sinArg2) + 2.0 * h + timeAnim);\n      flakePos = flakePos * 0.8 + 0.1 + fpos;\n\n      float toIntersection = dot(flakePos - pos, camK) * invRayDotCamK;\n      \n      if (toIntersection > 0.0) {\n        vec3 testPos = pos + ray * toIntersection - flakePos;\n        float testX = dot(testPos, camI);\n        float testY = dot(testPos, camJ);\n        vec2 testUV = abs(vec2(testX, testY));\n        \n        float depth = dot(flakePos - camPos, camK);\n        float flakeSize = max(uFlakeSize, uMinFlakeSize * depth * halfInvResX);\n        \n        // Avoid branching with step functions where possible\n        float dist;\n        if (uVariant < 0.5) {\n          dist = max(testUV.x, testUV.y);\n        } else if (uVariant < 1.5) {\n          dist = length(testUV);\n        } else {\n          float invFlakeSize = 1.0 / flakeSize;\n          dist = snowflakeDist(vec2(testX, testY) * invFlakeSize) * flakeSize;\n        }\n\n        if (dist < flakeSize) {\n          float flakeSizeRatio = uFlakeSize / flakeSize;\n          float intensity = exp2(-(t + toIntersection) * invDepthFade) *\n                           min(1.0, flakeSizeRatio * flakeSizeRatio) * uBrightness;\n          gl_FragColor = vec4(uColor * pow(vec3(intensity), vec3(uGamma)), 1.0);\n          return;\n        }\n      }\n    }\n\n    float nextStep = min(min(phase.x, phase.y), phase.z);\n    vec3 sel = step(phase, vec3(nextStep));\n    phase = phase - nextStep + strides * sel;\n    t += nextStep;\n    pos = mix(pos + ray * nextStep, floor(pos + ray * nextStep + 0.5), sel);\n  }\n\n  gl_FragColor = vec4(0.0);\n}\n`;\n\ninterface PixelSnowProps {\n    color?: string;\n    flakeSize?: number;\n    minFlakeSize?: number;\n    pixelResolution?: number;\n    speed?: number;\n    depthFade?: number;\n    farPlane?: number;\n    brightness?: number;\n    gamma?: number;\n    density?: number;\n    variant?: 'square' | 'round' | 'snowflake';\n    direction?: number;\n    className?: string;\n    style?: React.CSSProperties;\n}\n\nexport default function PixelSnow({\n    color = '#ffffff',\n    flakeSize = 0.01,\n    minFlakeSize = 1.25,\n    pixelResolution = 200,\n    speed = 1.25,\n    depthFade = 8,\n    farPlane = 20,\n    brightness = 1,\n    gamma = 0.4545,\n    density = 0.3,\n    variant = 'square',\n    direction = 125,\n    className = '',\n    style = {}\n}: PixelSnowProps) {\n    const containerRef = useRef<HTMLDivElement>(null);\n    const animationRef = useRef<number>(0);\n    const isVisibleRef = useRef(true);\n    const rendererRef = useRef<WebGLRenderer | null>(null);\n    const materialRef = useRef<ShaderMaterial | null>(null);\n    const resizeTimeoutRef = useRef<number | null>(null);\n\n    // Memoize shader variant value\n    const variantValue = useMemo(() => {\n        return variant === 'round' ? 1.0 : variant === 'snowflake' ? 2.0 : 0.0;\n    }, [variant]);\n\n    // Memoize color conversion\n    const colorVector = useMemo(() => {\n        const threeColor = new Color(color);\n        return new Vector3(threeColor.r, threeColor.g, threeColor.b);\n    }, [color]);\n\n    // Debounced resize handler\n    const handleResize = useCallback(() => {\n        if (resizeTimeoutRef.current) {\n            clearTimeout(resizeTimeoutRef.current);\n        }\n        resizeTimeoutRef.current = window.setTimeout(() => {\n            const container = containerRef.current;\n            const renderer = rendererRef.current;\n            const material = materialRef.current;\n            if (!container || !renderer || !material) return;\n\n            const w = container.offsetWidth;\n            const h = container.offsetHeight;\n            renderer.setSize(w, h);\n            material.uniforms.uResolution.value.set(w, h);\n        }, 100);\n    }, []);\n\n    // Visibility observer\n    useEffect(() => {\n        const container = containerRef.current;\n        if (!container) return;\n\n        const observer = new IntersectionObserver(\n            ([entry]) => {\n                isVisibleRef.current = entry.isIntersecting;\n            },\n            { threshold: 0 }\n        );\n\n        observer.observe(container);\n        return () => observer.disconnect();\n    }, []);\n\n    // Main Three.js setup - only runs once\n    useEffect(() => {\n        const container = containerRef.current;\n        if (!container) return;\n\n        const scene = new Scene();\n        const camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1);\n        const renderer = new WebGLRenderer({\n            antialias: false,\n            alpha: true,\n            premultipliedAlpha: false,\n            powerPreference: 'high-performance',\n            stencil: false,\n            depth: false\n        });\n\n        renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));\n        renderer.setSize(container.offsetWidth, container.offsetHeight);\n        renderer.setClearColor(0x000000, 0);\n        container.appendChild(renderer.domElement);\n        rendererRef.current = renderer;\n\n        const material = new ShaderMaterial({\n            vertexShader,\n            fragmentShader,\n            uniforms: {\n                uTime: { value: 0 },\n                uResolution: { value: new Vector2(container.offsetWidth, container.offsetHeight) },\n                uFlakeSize: { value: flakeSize },\n                uMinFlakeSize: { value: minFlakeSize },\n                uPixelResolution: { value: pixelResolution },\n                uSpeed: { value: speed },\n                uDepthFade: { value: depthFade },\n                uFarPlane: { value: farPlane },\n                uColor: { value: colorVector.clone() },\n                uBrightness: { value: brightness },\n                uGamma: { value: gamma },\n                uDensity: { value: density },\n                uVariant: { value: variantValue },\n                uDirection: { value: (direction * Math.PI) / 180 }\n            },\n            transparent: true\n        });\n        materialRef.current = material;\n\n        const geometry = new PlaneGeometry(2, 2);\n        scene.add(new Mesh(geometry, material));\n\n        window.addEventListener('resize', handleResize);\n\n        const startTime = performance.now();\n        const animate = () => {\n            animationRef.current = requestAnimationFrame(animate);\n\n            // Only render if visible\n            if (isVisibleRef.current) {\n                material.uniforms.uTime.value = (performance.now() - startTime) * 0.001;\n                renderer.render(scene, camera);\n            }\n        };\n        animate();\n\n        return () => {\n            cancelAnimationFrame(animationRef.current);\n            window.removeEventListener('resize', handleResize);\n            if (resizeTimeoutRef.current) {\n                clearTimeout(resizeTimeoutRef.current);\n            }\n            if (container.contains(renderer.domElement)) {\n                container.removeChild(renderer.domElement);\n            }\n            renderer.dispose();\n            geometry.dispose();\n            material.dispose();\n            rendererRef.current = null;\n            materialRef.current = null;\n        };\n    }, [handleResize]); // Only recreate scene when handleResize changes\n\n    // Update material uniforms when props change\n    useEffect(() => {\n        const material = materialRef.current;\n        if (!material) return;\n\n        material.uniforms.uFlakeSize.value = flakeSize;\n        material.uniforms.uMinFlakeSize.value = minFlakeSize;\n        material.uniforms.uPixelResolution.value = pixelResolution;\n        material.uniforms.uSpeed.value = speed;\n        material.uniforms.uDepthFade.value = depthFade;\n        material.uniforms.uFarPlane.value = farPlane;\n        material.uniforms.uBrightness.value = brightness;\n        material.uniforms.uGamma.value = gamma;\n        material.uniforms.uDensity.value = density;\n        material.uniforms.uVariant.value = variantValue;\n        material.uniforms.uDirection.value = (direction * Math.PI) / 180;\n        material.uniforms.uColor.value.copy(colorVector);\n    }, [\n        flakeSize,\n        minFlakeSize,\n        pixelResolution,\n        speed,\n        depthFade,\n        farPlane,\n        brightness,\n        gamma,\n        density,\n        variantValue,\n        direction,\n        colorVector\n    ]);\n\n    return (\n        <div\n            ref={containerRef}\n            className={`absolute inset-0 w-full h-full transform-gpu will-change-transform backface-hidden ${className}`}\n            style={style}\n        />\n    );\n}\n",
      "type": "registry:ui"
    }
  ]
}