{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "lightfall",
  "type": "registry:block",
  "title": "Lightfall",
  "description": "Lightfall",
  "files": [
    {
      "path": "components/usages/lightfallusage.tsx",
      "content": "import Lightfall from \"@/registry/open-source/lightfall\";\n\nexport default function Usage() {\n    return (\n        <div>\n            <Lightfall\n                colors={['#A6C8FF', '#5227FF', '#FF9FFC']}\n                backgroundColor=\"#0A29FF\"\n                speed={0.5}\n                streakCount={2}\n                streakWidth={1}\n                streakLength={1}\n                glow={1}\n                density={0.6}\n                twinkle={1}\n                zoom={3}\n                backgroundGlow={0.5}\n                opacity={1}\n                mouseInteraction\n                mouseStrength={0.5}\n                mouseRadius={1}\n                color1=\"#A6C8FF\"\n                color2=\"#5227FF\"\n                color3=\"#FF9FFC\"\n            />\t\t</div>\n    );\n}",
      "type": "registry:block",
      "target": "~/example.tsx"
    },
    {
      "path": "components/usages/lightfallusage.tsx",
      "content": "import Lightfall from \"@/registry/open-source/lightfall\";\n\nexport default function Usage() {\n    return (\n        <div>\n            <Lightfall\n                colors={['#A6C8FF', '#5227FF', '#FF9FFC']}\n                backgroundColor=\"#0A29FF\"\n                speed={0.5}\n                streakCount={2}\n                streakWidth={1}\n                streakLength={1}\n                glow={1}\n                density={0.6}\n                twinkle={1}\n                zoom={3}\n                backgroundGlow={0.5}\n                opacity={1}\n                mouseInteraction\n                mouseStrength={0.5}\n                mouseRadius={1}\n                color1=\"#A6C8FF\"\n                color2=\"#5227FF\"\n                color3=\"#FF9FFC\"\n            />\t\t</div>\n    );\n}",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/lightfall.tsx",
      "content": "import React, { useEffect, useRef } from 'react';\nimport { Renderer, Program, Mesh, Triangle } from 'ogl';\n\n// Credit:\n// https://www.reactbits.dev/backgrounds/lightfall\n\nexport interface LightfallProps {\n    className?: string;\n    dpr?: number;\n    paused?: boolean;\n    colors?: string[];\n    backgroundColor?: string;\n    speed?: number;\n    streakCount?: number;\n    streakWidth?: number;\n    streakLength?: number;\n    glow?: number;\n    density?: number;\n    twinkle?: number;\n    zoom?: number;\n    backgroundGlow?: number;\n    opacity?: number;\n    mouseInteraction?: boolean;\n    mouseStrength?: number;\n    mouseRadius?: number;\n    mouseDampening?: number;\n    mixBlendMode?: string;\n}\n\ntype RGB = [number, number, number];\n\nconst MAX_COLORS = 8;\n\nconst hexToRGB = (hex: string): RGB => {\n    const c = hex.replace('#', '').padEnd(6, '0');\n    const r = parseInt(c.slice(0, 2), 16) / 255;\n    const g = parseInt(c.slice(2, 4), 16) / 255;\n    const b = parseInt(c.slice(4, 6), 16) / 255;\n    return [r, g, b];\n};\n\nconst prepColors = (input?: string[]) => {\n    const base = (input && input.length ? input : ['#A6C8FF', '#5227FF', '#FF9FFC']).slice(0, MAX_COLORS);\n    const count = base.length;\n    const arr: RGB[] = [];\n    for (let i = 0; i < MAX_COLORS; i++) arr.push(hexToRGB(base[Math.min(i, base.length - 1)]));\n    const avg: RGB = [0, 0, 0];\n    for (let i = 0; i < count; i++) {\n        avg[0] += arr[i][0];\n        avg[1] += arr[i][1];\n        avg[2] += arr[i][2];\n    }\n    avg[0] /= count;\n    avg[1] /= count;\n    avg[2] /= count;\n    return { arr, count, avg };\n};\n\nconst vertex = `\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 vUv;\nvoid main() {\n  vUv = uv;\n  gl_Position = vec4(position, 0.0, 1.0);\n}\n`;\n\nconst fragment = `\nprecision highp float;\n\nuniform vec3  iResolution;\nuniform vec2  iMouse;\nuniform float iTime;\n\nuniform vec3  uColor0;\nuniform vec3  uColor1;\nuniform vec3  uColor2;\nuniform vec3  uColor3;\nuniform vec3  uColor4;\nuniform vec3  uColor5;\nuniform vec3  uColor6;\nuniform vec3  uColor7;\nuniform int   uColorCount;\n\nuniform vec3  uBgColor;\nuniform vec3  uMouseColor;\nuniform float uSpeed;\nuniform int   uStreakCount;\nuniform float uStreakWidth;\nuniform float uStreakLength;\nuniform float uGlow;\nuniform float uDensity;\nuniform float uTwinkle;\nuniform float uZoom;\nuniform float uBgGlow;\nuniform float uOpacity;\nuniform float uMouseEnabled;\nuniform float uMouseStrength;\nuniform float uMouseRadius;\n\nvarying vec2 vUv;\n\nvec3 palette(float h) {\n  int count = uColorCount;\n  if (count < 1) count = 1;\n  int idx = int(floor(clamp(h, 0.0, 0.999999) * float(count)));\n  if (idx <= 0) return uColor0;\n  if (idx == 1) return uColor1;\n  if (idx == 2) return uColor2;\n  if (idx == 3) return uColor3;\n  if (idx == 4) return uColor4;\n  if (idx == 5) return uColor5;\n  if (idx == 6) return uColor6;\n  return uColor7;\n}\n\nvec3 tanhv(vec3 x) {\n  vec3 e = exp(-2.0 * x);\n  return (1.0 - e) / (1.0 + e);\n}\n\nvec2 sceneC(vec2 frag, vec2 r) {\n  vec2 P = (frag + frag - r) / r.x;\n  float z = 0.0;\n  float d = 1e3;\n  vec4 O = vec4(0.0);\n  for (int k = 0; k < 39; k++) {\n    if (d <= 1e-4) break;\n    O = z * normalize(vec4(P, uZoom, 0.0)) - vec4(0.0, 4.0, 1.0, 0.0) / 4.5;\n    d = 1.0 - sqrt(length(O * O));\n    z += d;\n  }\n  return vec2(O.x, atan(O.z, O.y));\n}\n\nvoid mainImage(out vec4 o, vec2 C) {\n  vec2 r = iResolution.xy;\n  vec2 uv0 = (C + C - r) / r.x;\n  float T = 0.1 * iTime * uSpeed + 9.0;\n  float angRings = max(1.0, floor(6.28318530718 * max(uDensity, 0.05) + 0.5));\n  vec2 Y = vec2(5e-3, 6.28318530718 / angRings);\n\n  vec2 c0 = sceneC(C, r);\n  vec2 cdx = sceneC(C + vec2(1.0, 0.0), r);\n  vec2 cdy = sceneC(C + vec2(0.0, 1.0), r);\n  vec2 dCx = cdx - c0;\n  vec2 dCy = cdy - c0;\n  dCx.y -= 6.28318530718 * floor(dCx.y / 6.28318530718 + 0.5);\n  dCy.y -= 6.28318530718 * floor(dCy.y / 6.28318530718 + 0.5);\n  vec2 fw = abs(dCx) + abs(dCy);\n  C = c0;\n\n  vec2 P = vec2(2.0, 1.0) * uv0 - (r / r.x) * vec2(0.0, 1.0);\n  vec4 O = vec4(uBgColor * 90.0 * uBgGlow / (1e3 * dot(P, P) + 6.0), 0.0);\n\n  float mGlow = 0.0;\n  if (uMouseEnabled > 0.5) {\n    vec2 mN = (iMouse + iMouse - r) / r.x;\n    float md = length(uv0 - mN);\n    mGlow = exp(-md * md / max(uMouseRadius * uMouseRadius, 1e-4)) * uMouseStrength;\n    O.rgb += uMouseColor * mGlow * 0.25;\n  }\n\n  float zr = 5e-4 * uStreakWidth;\n  vec2 rr = vec2(max(length(fw), 1e-5));\n  float tail = 19.0 / max(uStreakLength, 0.05);\n\n  for (int m = 0; m < 16; m++) {\n    if (m >= uStreakCount) break;\n    float jf = float(m) + 1.0;\n    float ic = fract(sin(dot(vec2(jf, floor(C.x / Y.x + 0.5)), vec2(7.0, 11.0)) * 73.0));\n    vec2 Pp = C - (T + T * ic) * vec2(0.0, 1.0);\n    Pp -= floor(Pp / Y + 0.5) * Y;\n    float h = fract(8663.0 * ic);\n    vec3 col = palette(h);\n    float weight = mix(1.5, 1.0 + sin(T + 7.0 * h + 4.0), uTwinkle);\n    weight *= (1.0 + mGlow * 2.0);\n    vec2 inner = vec2(length(max(Pp, vec2(-1.0, 0.0))), length(Pp) - zr) - zr;\n    vec2 sm = vec2(1.0) - smoothstep(-rr, rr, inner);\n    O.rgb += dot(sm, vec2(exp(tail * Pp.y), 3.0)) * col * weight;\n    C.x += Y.x / 8.0;\n  }\n\n  vec3 colr = sqrt(tanhv(max(O.rgb * uGlow - vec3(0.04, 0.08, 0.02), 0.0)));\n  o = vec4(colr, uOpacity);\n}\n\nvoid main() {\n  vec4 color;\n  mainImage(color, vUv * iResolution.xy);\n  gl_FragColor = color;\n}\n`;\n\nconst Lightfall: React.FC<LightfallProps> = ({\n    className,\n    dpr,\n    paused = false,\n    colors = ['#A6C8FF', '#5227FF', '#FF9FFC'],\n    backgroundColor = '#0A29FF',\n    speed = 0.5,\n    streakCount = 2,\n    streakWidth = 1,\n    streakLength = 1,\n    glow = 1,\n    density = 0.6,\n    twinkle = 1,\n    zoom = 3,\n    backgroundGlow = 0.5,\n    opacity = 1,\n    mouseInteraction = true,\n    mouseStrength = 0.5,\n    mouseRadius = 1,\n    mouseDampening = 0.15,\n    mixBlendMode\n}) => {\n    const containerRef = useRef<HTMLDivElement | null>(null);\n    const rafRef = useRef<number | null>(null);\n    const programRef = useRef<Program | null>(null);\n    const meshRef = useRef<Mesh | null>(null);\n    const geometryRef = useRef<Triangle | null>(null);\n    const rendererRef = useRef<Renderer | null>(null);\n    const mouseTargetRef = useRef<[number, number]>([0, 0]);\n    const lastTimeRef = useRef(0);\n\n    useEffect(() => {\n        const container = containerRef.current;\n        if (!container) return;\n\n        const renderer = new Renderer({\n            dpr: dpr ?? (typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1),\n            alpha: true,\n            antialias: true\n        });\n        rendererRef.current = renderer;\n        const gl = renderer.gl;\n        const canvas = gl.canvas as HTMLCanvasElement;\n\n        canvas.style.width = '100%';\n        canvas.style.height = '100%';\n        canvas.style.display = 'block';\n        container.appendChild(canvas);\n\n        const { arr, count, avg } = prepColors(colors);\n\n        const uniforms = {\n            iResolution: { value: [gl.drawingBufferWidth, gl.drawingBufferHeight, 1] },\n            iMouse: { value: [0, 0] },\n            iTime: { value: 0 },\n            uColor0: { value: arr[0] },\n            uColor1: { value: arr[1] },\n            uColor2: { value: arr[2] },\n            uColor3: { value: arr[3] },\n            uColor4: { value: arr[4] },\n            uColor5: { value: arr[5] },\n            uColor6: { value: arr[6] },\n            uColor7: { value: arr[7] },\n            uColorCount: { value: count },\n            uBgColor: { value: hexToRGB(backgroundColor) },\n            uMouseColor: { value: avg },\n            uSpeed: { value: speed },\n            uStreakCount: { value: Math.max(1, Math.min(16, Math.round(streakCount))) },\n            uStreakWidth: { value: streakWidth },\n            uStreakLength: { value: streakLength },\n            uGlow: { value: glow },\n            uDensity: { value: density },\n            uTwinkle: { value: twinkle },\n            uZoom: { value: zoom },\n            uBgGlow: { value: backgroundGlow },\n            uOpacity: { value: opacity },\n            uMouseEnabled: { value: mouseInteraction ? 1 : 0 },\n            uMouseStrength: { value: mouseStrength },\n            uMouseRadius: { value: mouseRadius }\n        };\n\n        const program = new Program(gl, { vertex, fragment, uniforms });\n        programRef.current = program;\n\n        const geometry = new Triangle(gl);\n        geometryRef.current = geometry;\n        const mesh = new Mesh(gl, { geometry, program });\n        meshRef.current = mesh;\n\n        const resize = () => {\n            const rect = container.getBoundingClientRect();\n            renderer.setSize(rect.width, rect.height);\n            uniforms.iResolution.value = [gl.drawingBufferWidth, gl.drawingBufferHeight, 1];\n        };\n\n        resize();\n        const ro = new ResizeObserver(resize);\n        ro.observe(container);\n\n        const onPointerMove = (e: PointerEvent) => {\n            const rect = canvas.getBoundingClientRect();\n            const scale = renderer.dpr || 1;\n            const x = (e.clientX - rect.left) * scale;\n            const y = (rect.height - (e.clientY - rect.top)) * scale;\n            mouseTargetRef.current = [x, y];\n            if (mouseDampening <= 0) {\n                uniforms.iMouse.value = [x, y];\n            }\n        };\n        if (mouseInteraction) {\n            canvas.addEventListener('pointermove', onPointerMove);\n        }\n\n        const loop = (t: number) => {\n            rafRef.current = requestAnimationFrame(loop);\n            uniforms.iTime.value = t * 0.001;\n            if (mouseDampening > 0) {\n                if (!lastTimeRef.current) lastTimeRef.current = t;\n                const dt = (t - lastTimeRef.current) / 1000;\n                lastTimeRef.current = t;\n                const tau = Math.max(1e-4, mouseDampening);\n                let factor = 1 - Math.exp(-dt / tau);\n                if (factor > 1) factor = 1;\n                const target = mouseTargetRef.current;\n                const cur = uniforms.iMouse.value as number[];\n                cur[0] += (target[0] - cur[0]) * factor;\n                cur[1] += (target[1] - cur[1]) * factor;\n            } else {\n                lastTimeRef.current = t;\n            }\n            if (!paused && programRef.current && meshRef.current) {\n                try {\n                    renderer.render({ scene: meshRef.current });\n                } catch (e) {\n                    console.error(e);\n                }\n            }\n        };\n        rafRef.current = requestAnimationFrame(loop);\n\n        return () => {\n            if (rafRef.current) cancelAnimationFrame(rafRef.current);\n            if (mouseInteraction) canvas.removeEventListener('pointermove', onPointerMove);\n            ro.disconnect();\n            if (canvas.parentElement === container) {\n                container.removeChild(canvas);\n            }\n            const callIfFn = (obj: unknown, key: string) => {\n                const fn = obj && (obj as Record<string, unknown>)[key];\n                if (typeof fn === 'function') {\n                    (fn as () => void).call(obj);\n                }\n            };\n            callIfFn(programRef.current, 'remove');\n            callIfFn(geometryRef.current, 'remove');\n            callIfFn(meshRef.current, 'remove');\n            callIfFn(rendererRef.current, 'destroy');\n            programRef.current = null;\n            geometryRef.current = null;\n            meshRef.current = null;\n            rendererRef.current = null;\n        };\n    }, [\n        dpr,\n        paused,\n        colors,\n        backgroundColor,\n        speed,\n        streakCount,\n        streakWidth,\n        streakLength,\n        glow,\n        density,\n        twinkle,\n        zoom,\n        backgroundGlow,\n        opacity,\n        mouseInteraction,\n        mouseStrength,\n        mouseRadius,\n        mouseDampening\n    ]);\n\n    return (\n        <div\n            ref={containerRef}\n            className={`w-full h-full overflow-hidden relative ${className ?? ''}`}\n            style={{\n                ...(mixBlendMode && { mixBlendMode: mixBlendMode as React.CSSProperties['mixBlendMode'] })\n            }}\n        />\n    );\n};\n\nexport default Lightfall;\n",
      "type": "registry:ui"
    }
  ]
}