{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "light-speed",
  "title": "Light Speed",
  "description": "A Warp/Light-Speed hyperspace animation inspired by the Ducky3D Blender tutorial.",
  "dependencies": [
    "three",
    "@react-three/fiber",
    "@react-three/postprocessing",
    "clsx",
    "tailwind-merge"
  ],
  "files": [
    {
      "path": "registry/chamaac/light-speed/light-speed.tsx",
      "content": "\"use client\";\n\nimport React, { useRef, useMemo } from \"react\";\nimport { Canvas, useFrame } from \"@react-three/fiber\";\nimport * as THREE from \"three\";\nimport { EffectComposer, Bloom } from \"@react-three/postprocessing\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface LightSpeedProps {\n  /**\n   * Number of particles (stars/lines).\n   * @default 2000\n   */\n  particleCount?: number;\n  /**\n   * Base speed of the warp effect.\n   * @default 4\n   */\n  speed?: number;\n  /**\n   * Base color of the emitted light streaks.\n   * @default \"#33b2ff\"\n   */\n  lightColor?: string;\n  /**\n   * Intensity of the bloom glow.\n   * @default 3.0\n   */\n  intensity?: number;\n  /**\n   * Extent of the cylinder radius in which particles spawn.\n   * @default 25\n   */\n  radius?: number;\n  /**\n   * Length of the cylinder before particles loop back.\n   * @default 150\n   */\n  cylinderLength?: number;\n  className?: string;\n}\n\nfunction Particles({\n  count,\n  baseSpeed,\n  lightColor,\n  intensity,\n  radius,\n  cylinderLength,\n}: {\n  count: number;\n  baseSpeed: number;\n  lightColor: string;\n  intensity: number;\n  radius: number;\n  cylinderLength: number;\n}) {\n  const meshRef = useRef<THREE.InstancedMesh>(null);\n\n  // Object3D to help apply transforms to individual instanced items\n  const dummy = useMemo(() => new THREE.Object3D(), []);\n\n  // Initialize particle properties\n  const particles = useMemo(() => {\n    const temp = [];\n    for (let i = 0; i < count; i++) {\n      const angle = Math.random() * Math.PI * 2;\n      // Start further away from the center to leave a \"tunnel\" for the camera\n      const r = 2 + Math.random() * (radius - 2);\n      const x = Math.cos(angle) * r;\n      const y = Math.sin(angle) * r;\n      let z = (Math.random() - 0.5) * cylinderLength;\n      let speedMultiplier = 0.5 + Math.random() * 0.5;\n\n      // Pre-warm the particle simulation by 1.5 seconds \n      for (let j = 0; j < 90; j++) {\n        z += baseSpeed * speedMultiplier * (1 / 60) * 50;\n        if (z > 5) {\n          z = -cylinderLength / 2;\n          speedMultiplier = 0.5 + Math.random() * 0.5;\n        }\n      }\n\n      temp.push({\n        x,\n        y,\n        z,\n        // Individual random speed multiplier to give depth variation\n        speedMultiplier,\n        // Individual random length to look more organic\n        length: 1 + Math.random() * 2,\n        angle,\n        radius: r,\n      });\n    }\n    return temp;\n  }, [count, radius, cylinderLength]);\n\n  const bloomColor = useMemo(() => {\n    const color = new THREE.Color(lightColor);\n    color.multiplyScalar(intensity);\n    return color;\n  }, [lightColor, intensity]);\n\n  useFrame((state, delta) => {\n    if (!meshRef.current) return;\n\n    // We update each particle's matrix and apply rotation/translation\n    particles.forEach((particle, i) => {\n      // Move particle towards the camera (+Z)\n      // delta makes the movement frame-rate independent.\n      const moveDistance = baseSpeed * particle.speedMultiplier * delta * 50;\n      particle.z += moveDistance;\n\n      // If a particle passes the camera (z > 5), loop it back to the far end\n      if (particle.z > 5) {\n        particle.z = -cylinderLength / 2;\n        particle.speedMultiplier = 0.5 + Math.random() * 0.5;\n      }\n\n      // We place the dummy at the particle coordinates\n      dummy.position.set(particle.x, particle.y, particle.z);\n      // We scale the length on the Z-axis to mimic motion blur/stretched UV spheres\n      // The faster it moves, the more stretched it appears\n      const stretchZ = particle.length + (baseSpeed * particle.speedMultiplier * 0.5);\n      // X and Y are scaled small to look thin (like streaks)\n      dummy.scale.set(0.04, 0.04, stretchZ);\n\n      dummy.updateMatrix();\n      meshRef.current!.setMatrixAt(i, dummy.matrix);\n    });\n\n    // Tell Three.js the matrix data has been updated and deserves a re-render\n    meshRef.current.instanceMatrix.needsUpdate = true;\n  });\n\n  return (\n    <instancedMesh\n      ref={meshRef}\n      args={[undefined, undefined, count]}\n      frustumCulled={false}\n    >\n      <sphereGeometry args={[1, 8, 8]} />\n      <meshBasicMaterial\n        color={bloomColor}\n        toneMapped={false}\n        transparent\n        opacity={0.9}\n      />\n    </instancedMesh>\n  );\n}\n\nexport function LightSpeed({\n  particleCount = 1000,\n  speed = 2.4,\n  lightColor = \"#b026ff\",\n  intensity = 3.0,\n  radius = 25,\n  cylinderLength = 150,\n  className,\n}: LightSpeedProps) {\n  return (\n    <div\n      className={cn(\n        \"absolute inset-0 w-full h-full pointer-events-none overflow-hidden bg-[#05070b]\",\n        className\n      )}\n    >\n      {/* \n        Camera looks down -Z axis. FOV 90 for a wider warping look.\n        It simulates a camera traveling in a cylinder (as per the Ducky3D video). \n      */}\n      <Canvas camera={{ position: [0, 0, 5], fov: 90 }} dpr={[1, 2]}>\n\n        {/* The fog acts as the \"Volume cube\" from the tutorial to fade out clipping edges in the distance */}\n        <fogExp2 attach=\"fog\" args={[\"#000000\", 0.025]} />\n        <color attach=\"background\" args={[\"#000000\"]} />\n\n        <Particles\n          count={particleCount}\n          baseSpeed={speed}\n          lightColor={lightColor}\n          intensity={intensity}\n          radius={radius}\n          cylinderLength={cylinderLength}\n        />\n\n        <EffectComposer>\n          {/* Bloom takes any value > 1 and makes it emit a neon outer glow */}\n          <Bloom\n            luminanceThreshold={0.1}\n            luminanceSmoothing={0.9}\n            intensity={1.5}\n            mipmapBlur\n          />\n        </EffectComposer>\n      </Canvas>\n    </div>\n  );\n}\n\nexport default LightSpeed;\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}