{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "game-of-life",
  "type": "registry:ui",
  "title": "Game of Life",
  "description": "A game of life component for Next.js apps with next-themes and Tailwind CSS, supporting system, light, and dark modes.",
  "files": [
    {
      "path": "registry/bucharitesh/game-of-life.tsx",
      "content": "'use client';\n\nimport { cn } from '@/lib/utils';\nimport * as React from 'react';\n\ninterface GameOfLifeProps\n  extends React.CanvasHTMLAttributes<HTMLCanvasElement> {\n  size?: number;\n  interval?: number;\n  backgroundColor?: string;\n  cellColor?: string;\n  density?: number; // Value between 0 and 1, default 0.1 (10% cells alive)\n}\n\nconst GameOfLife = React.forwardRef<HTMLCanvasElement, GameOfLifeProps>(\n  (\n    {\n      className,\n      size = 12,\n      interval = 150,\n      backgroundColor = '#000000',\n      cellColor = '#1e1e1e',\n      density = 0.1,\n      ...props\n    },\n    ref\n  ) => {\n    const canvasRef = React.useRef<HTMLCanvasElement | null>(null);\n    const frameRef = React.useRef<number>(0);\n    const gridRef = React.useRef<boolean[][]>([]);\n    const lastUpdateRef = React.useRef<number>(0);\n    const transitionRef = React.useRef<{\n      from: boolean[][];\n      to: boolean[][];\n      progress: number;\n    } | null>(null);\n    const [isReady, setIsReady] = React.useState(false);\n    const isInitialRender = React.useRef(true);\n\n    React.useEffect(() => {\n      const canvas = canvasRef.current;\n      if (!canvas) return;\n\n      const ctx = canvas.getContext('2d', { alpha: false });\n      if (!ctx) return;\n\n      const parent = canvas.parentElement;\n      if (!parent) return;\n\n      // Keep size constant\n      const cellSize = size;\n      let width = parent.clientWidth;\n      let height = parent.clientHeight;\n      let cols = Math.floor(width / cellSize);\n      let rows = Math.floor(height / cellSize);\n\n      const createGrid = (): boolean[][] => {\n        const parent = canvas.parentElement;\n        if (!parent)\n          return Array.from({ length: cols }, () =>\n            new Array(rows).fill(false)\n          );\n\n        width = parent.clientWidth;\n        height = parent.clientHeight;\n        cols = Math.floor(width / cellSize);\n        rows = Math.floor(height / cellSize);\n\n        // Update canvas size to match parent\n        canvas.width = width;\n        canvas.height = height;\n\n        // Create a random initial pattern based on density prop\n        const grid = Array.from({ length: cols }, () =>\n          Array.from({ length: rows }, () => Math.random() < density)\n        );\n\n        return grid;\n      };\n\n      const updateGrid = (grid: boolean[][]): boolean[][] => {\n        const next: boolean[][] = Array.from({ length: cols }, () =>\n          new Array(rows).fill(false)\n        );\n\n        for (let i = 0; i < cols; i++) {\n          for (let j = 0; j < rows; j++) {\n            let neighbors = 0;\n\n            for (let dx = -1; dx <= 1; dx++) {\n              for (let dy = -1; dy <= 1; dy++) {\n                if (dx === 0 && dy === 0) continue;\n                const nx = (i + dx + cols) % cols;\n                const ny = (j + dy + rows) % rows;\n                neighbors += grid[nx][ny] ? 1 : 0;\n              }\n            }\n\n            next[i][j] = neighbors === 3 || (grid[i][j] && neighbors === 2);\n          }\n        }\n\n        return next;\n      };\n\n      const interpolateGrids = (fromGrid: boolean[][], toGrid: boolean[][]) => {\n        ctx.fillStyle = backgroundColor;\n        ctx.fillRect(0, 0, width, height);\n\n        for (let i = 0; i < cols; i++) {\n          for (let j = 0; j < rows; j++) {\n            const fromState = fromGrid[i][j];\n            const toState = toGrid[i][j];\n\n            if (fromState || toState) {\n              ctx.fillStyle = cellColor;\n              ctx.fillRect(i * cellSize, j * cellSize, cellSize, cellSize);\n            }\n          }\n        }\n      };\n\n      const render = (grid: boolean[][]) => {\n        ctx.fillStyle = backgroundColor;\n        ctx.fillRect(0, 0, width, height);\n\n        ctx.fillStyle = cellColor;\n\n        for (let i = 0; i < cols; i++) {\n          for (let j = 0; j < rows; j++) {\n            if (grid[i][j]) {\n              ctx.fillRect(i * size, j * size, size, size);\n            }\n          }\n        }\n      };\n\n      const startTransition = (fromGrid: boolean[][], toGrid: boolean[][]) => {\n        transitionRef.current = {\n          from: fromGrid.map((row) => [...row]),\n          to: toGrid.map((row) => [...row]),\n          progress: 0,\n        };\n      };\n\n      const animate = (timestamp: number) => {\n        if (timestamp - lastUpdateRef.current >= interval) {\n          if (gridRef.current) {\n            const nextGrid = updateGrid(gridRef.current);\n            startTransition(gridRef.current, nextGrid);\n            gridRef.current = nextGrid;\n          }\n          lastUpdateRef.current = timestamp;\n        }\n\n        if (transitionRef.current) {\n          const { from, to } = transitionRef.current;\n          interpolateGrids(from, to);\n\n          transitionRef.current.progress += 0.1;\n          if (transitionRef.current.progress >= 1) {\n            transitionRef.current = null;\n            render(gridRef.current);\n          }\n        }\n\n        frameRef.current = requestAnimationFrame(animate);\n      };\n\n      let resizeTimeout: NodeJS.Timeout;\n      const handleResize = () => {\n        clearTimeout(resizeTimeout);\n        resizeTimeout = setTimeout(() => {\n          const parent = canvasRef.current?.parentElement;\n          if (!parent) return;\n\n          setIsReady(false);\n          const newGrid = createGrid();\n\n          if (gridRef.current) {\n            startTransition(gridRef.current, newGrid);\n          }\n\n          gridRef.current = newGrid;\n          setTimeout(() => setIsReady(true), 50);\n        }, 250);\n      };\n\n      gridRef.current = createGrid();\n      lastUpdateRef.current = performance.now();\n      frameRef.current = requestAnimationFrame(animate);\n      window.addEventListener('resize', handleResize);\n      setIsReady(true);\n      isInitialRender.current = false;\n\n      return () => {\n        cancelAnimationFrame(frameRef.current);\n        window.removeEventListener('resize', handleResize);\n        clearTimeout(resizeTimeout);\n      };\n    }, [size, interval, backgroundColor, cellColor]);\n\n    return (\n      <canvas\n        ref={React.useMemo(\n          () => (node: HTMLCanvasElement | null) => {\n            if (typeof ref === 'function') ref(node);\n            else if (ref) ref.current = node;\n            canvasRef.current = node;\n          },\n          [ref]\n        )}\n        className={cn(\n          'absolute inset-0 h-full w-full transition-opacity duration-500',\n          isReady ? 'opacity-100' : 'opacity-0',\n          className\n        )}\n        {...props}\n      />\n    );\n  }\n);\n\nGameOfLife.displayName = 'GameOfLife';\n\nexport { GameOfLife };\nexport type { GameOfLifeProps };\n",
      "type": "registry:ui"
    }
  ]
}