{
  "name": "beams-background",
  "type": "registry:component",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "type": "registry:component",
      "content": "\"use client\";\n\n/**\n * @author: @dorianbaffier\n * @description: Beams Background\n * @version: 1.0.0\n * @date: 2025-06-26\n * @license: MIT\n * @website: https://kokonutui.com\n * @github: https://github.com/kokonut-labs/kokonutui\n */\n\nimport { motion } from \"motion/react\";\nimport { useEffect, useRef } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\ninterface AnimatedGradientBackgroundProps {\n  className?: string;\n  children?: React.ReactNode;\n  intensity?: \"subtle\" | \"medium\" | \"strong\";\n}\n\ninterface Beam {\n  x: number;\n  y: number;\n  width: number;\n  length: number;\n  angle: number;\n  speed: number;\n  opacity: number;\n  hue: number;\n  pulse: number;\n  pulseSpeed: number;\n}\n\nfunction createBeam(width: number, height: number, isDarkMode: boolean): Beam {\n  const angle = -35 + Math.random() * 10;\n  const hueBase = isDarkMode ? 190 : 210;\n  const hueRange = isDarkMode ? 70 : 50;\n\n  return {\n    x: Math.random() * width * 1.5 - width * 0.25,\n    y: Math.random() * height * 1.5 - height * 0.25,\n    width: 30 + Math.random() * 60,\n    length: height * 2.5,\n    angle,\n    speed: 0.6 + Math.random() * 1.2,\n    opacity: 0.12 + Math.random() * 0.16,\n    hue: hueBase + Math.random() * hueRange,\n    pulse: Math.random() * Math.PI * 2,\n    pulseSpeed: 0.02 + Math.random() * 0.03,\n  };\n}\n\nexport default function BeamsBackground({\n  className,\n  intensity = \"strong\",\n}: AnimatedGradientBackgroundProps) {\n  const canvasRef = useRef<HTMLCanvasElement>(null);\n  const beamsRef = useRef<Beam[]>([]);\n  const animationFrameRef = useRef<number>(0);\n  const MINIMUM_BEAMS = 20;\n  const isDarkModeRef = useRef<boolean>(false);\n\n  const opacityMap = {\n    subtle: 0.7,\n    medium: 0.85,\n    strong: 1,\n  };\n\n  useEffect(() => {\n    const canvas = canvasRef.current;\n    if (!canvas) return;\n\n    const ctx = canvas.getContext(\"2d\");\n    if (!ctx) return;\n\n    // Check for dark mode\n    const updateDarkMode = () => {\n      isDarkModeRef.current =\n        document.documentElement.classList.contains(\"dark\");\n    };\n\n    const observer = new MutationObserver(updateDarkMode);\n    observer.observe(document.documentElement, {\n      attributes: true,\n      attributeFilter: [\"class\"],\n    });\n\n    updateDarkMode();\n\n    const updateCanvasSize = () => {\n      const dpr = window.devicePixelRatio || 1;\n      canvas.width = window.innerWidth * dpr;\n      canvas.height = window.innerHeight * dpr;\n      canvas.style.width = `${window.innerWidth}px`;\n      canvas.style.height = `${window.innerHeight}px`;\n      ctx.scale(dpr, dpr);\n\n      const totalBeams = MINIMUM_BEAMS * 1.5;\n      beamsRef.current = Array.from({ length: totalBeams }, () =>\n        createBeam(canvas.width, canvas.height, isDarkModeRef.current)\n      );\n    };\n\n    updateCanvasSize();\n    window.addEventListener(\"resize\", updateCanvasSize);\n\n    function resetBeam(beam: Beam, index: number, totalBeams: number) {\n      if (!canvas) return beam;\n\n      const column = index % 3;\n      const spacing = canvas.width / 3;\n\n      const hueBase = isDarkModeRef.current ? 190 : 210;\n      const hueRange = isDarkModeRef.current ? 70 : 50;\n\n      beam.y = canvas.height + 100;\n      beam.x =\n        column * spacing + spacing / 2 + (Math.random() - 0.5) * spacing * 0.5;\n      beam.width = 100 + Math.random() * 100;\n      beam.speed = 0.5 + Math.random() * 0.4;\n      beam.hue = hueBase + (index * hueRange) / totalBeams;\n      beam.opacity = 0.2 + Math.random() * 0.1;\n      return beam;\n    }\n\n    function drawBeam(ctx: CanvasRenderingContext2D, beam: Beam) {\n      ctx.save();\n      ctx.translate(beam.x, beam.y);\n      ctx.rotate((beam.angle * Math.PI) / 180);\n\n      const pulsingOpacity =\n        beam.opacity *\n        (0.8 + Math.sin(beam.pulse) * 0.2) *\n        opacityMap[intensity];\n\n      const gradient = ctx.createLinearGradient(0, 0, 0, beam.length);\n\n      const saturation = isDarkModeRef.current ? \"85%\" : \"75%\";\n      const lightness = isDarkModeRef.current ? \"65%\" : \"45%\";\n\n      gradient.addColorStop(\n        0,\n        `hsla(${beam.hue}, ${saturation}, ${lightness}, 0)`\n      );\n      gradient.addColorStop(\n        0.1,\n        `hsla(${beam.hue}, ${saturation}, ${lightness}, ${\n          pulsingOpacity * 0.5\n        })`\n      );\n      gradient.addColorStop(\n        0.4,\n        `hsla(${beam.hue}, ${saturation}, ${lightness}, ${pulsingOpacity})`\n      );\n      gradient.addColorStop(\n        0.6,\n        `hsla(${beam.hue}, ${saturation}, ${lightness}, ${pulsingOpacity})`\n      );\n      gradient.addColorStop(\n        0.9,\n        `hsla(${beam.hue}, ${saturation}, ${lightness}, ${\n          pulsingOpacity * 0.5\n        })`\n      );\n      gradient.addColorStop(\n        1,\n        `hsla(${beam.hue}, ${saturation}, ${lightness}, 0)`\n      );\n\n      ctx.fillStyle = gradient;\n      ctx.fillRect(-beam.width / 2, 0, beam.width, beam.length);\n      ctx.restore();\n    }\n\n    function animate() {\n      if (!(canvas && ctx)) return;\n\n      ctx.clearRect(0, 0, canvas.width, canvas.height);\n      ctx.filter = \"blur(35px)\";\n\n      const totalBeams = beamsRef.current.length;\n      beamsRef.current.forEach((beam, index) => {\n        beam.y -= beam.speed;\n        beam.pulse += beam.pulseSpeed;\n\n        // Reset beam when it goes off screen\n        if (beam.y + beam.length < -100) {\n          resetBeam(beam, index, totalBeams);\n        }\n\n        drawBeam(ctx, beam);\n      });\n\n      animationFrameRef.current = requestAnimationFrame(animate);\n    }\n\n    animate();\n\n    return () => {\n      window.removeEventListener(\"resize\", updateCanvasSize);\n      if (animationFrameRef.current) {\n        cancelAnimationFrame(animationFrameRef.current);\n      }\n      observer.disconnect();\n    };\n  }, [intensity]);\n\n  return (\n    <div\n      className={cn(\n        \"relative min-h-screen w-full overflow-hidden bg-neutral-100 dark:bg-neutral-950\",\n        className\n      )}\n    >\n      <canvas\n        className=\"absolute inset-0\"\n        ref={canvasRef}\n        style={{ filter: \"blur(15px)\" }}\n      />\n\n      <motion.div\n        animate={{\n          opacity: [0.05, 0.15, 0.05],\n        }}\n        className=\"absolute inset-0 bg-neutral-900/5 dark:bg-neutral-950/5\"\n        style={{\n          backdropFilter: \"blur(50px)\",\n        }}\n        transition={{\n          duration: 10,\n          ease: \"easeInOut\",\n          repeat: Number.POSITIVE_INFINITY,\n        }}\n      />\n\n      <div className=\"relative z-10 flex h-screen w-full items-center justify-center\">\n        <div className=\"flex flex-col items-center justify-center gap-6 px-4 text-center\">\n          <motion.h1\n            animate={{ opacity: 1, y: 0 }}\n            className=\"font-semibold text-6xl text-neutral-900 tracking-tighter md:text-7xl lg:text-8xl dark:text-white\"\n            initial={{ opacity: 0, y: 20 }}\n            transition={{ duration: 0.8 }}\n          >\n            Beams\n            <br />\n            Background\n          </motion.h1>\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "path": "/components/kokonutui/beams-background.tsx",
      "target": "components/kokonutui/beams-background.tsx"
    }
  ]
}