{
  "slug": "noise",
  "name": "Noise Background",
  "description": "High-performance procedural grain noise background with CRT static, film dust, vignette, and scanlines.",
  "summary": "An ultra-optimized hardware-accelerated procedural canvas noise generator engineered for cinematic backgrounds and atmosphere. Features sub-pixel pattern tiling via 32-bit Uint32 buffer rendering, 3 distinct procedural grain algorithms (film grain, analog static, and film dust), edge vignette darkening, CRT scanline rasterization, and automatic viewport/container adaptation with IntersectionObserver and reduced-motion support.",
  "category": "backgrounds-and-effects",
  "tags": [
    "noise",
    "grain",
    "canvas",
    "background",
    "texture",
    "vignette",
    "scanlines"
  ],
  "dependencies": [
    "clsx",
    "tailwind-merge"
  ],
  "version": "1.0.0",
  "createdDate": "2026-09-23",
  "updatedDate": "2026-09-23",
  "interactive": false,
  "supportsColor": false,
  "highlights": [
    "Sub-pixel pattern tiling running on 32-bit Uint32Array memory buffers for near-zero CPU overhead",
    "Three selectable noise algorithms: Film Grain, CRT Static, and Film Dust",
    "Built-in cinematic radial vignette and retro CRT scanline raster layers",
    "Seamless container adaptation supporting both fixed fullscreen and container-bounded layouts",
    "Automatic battery-saving lifecycle management with IntersectionObserver and document.hidden pauses",
    "Full accessibility with automatic prefers-reduced-motion detection and animation freezing"
  ],
  "anatomy": [
    "<div> (Responsive Container: handles ResizeObserver and IntersectionObserver lifecycle)",
    "<canvas> (2D Canvas Buffer: renders high-density tiled noise patterns without CPU overhead)",
    "<div> (Cinematic Vignette: radial gradient edge darkening layer)",
    "<div> (Scanline Overlay: repeating-linear-gradient CRT monitor raster lines)"
  ],
  "physics": {
    "engine": "32-bit Uint32 Procedural Buffer + 2D Canvas Matrix Pattern Transform",
    "description": "Direct memory buffer generation coupled with GPU hardware-accelerated 2D canvas pattern matrix translation and jitter.",
    "parameters": [
      {
        "label": "Pattern Buffer Size",
        "value": "250px × 250px"
      },
      {
        "label": "Memory Pipeline",
        "value": "32-bit Uint32Array"
      },
      {
        "label": "Frame Rate Cap",
        "value": "30 FPS default (fps prop)"
      },
      {
        "label": "Refresh Interval",
        "value": "1 (every rendered frame) default"
      }
    ]
  },
  "accessibility": {
    "aria": "No role or aria-hidden is set. Extra props are spread onto the root div, so pass aria-hidden=\"true\" for purely decorative use without children.",
    "reducedMotion": "Automatically stops animated pattern jitter and freezes grain texture when prefers-reduced-motion is active."
  },
  "guidelines": {
    "recommended": [
      "Cinematic dark mode hero backgrounds in developer platforms, dashboards, and landing pages",
      "Subtle analogue film grain texture inside card surfaces, modals, and container boundaries",
      "Terminal, retro, or synthesizer interfaces utilizing CRT static and scanlines"
    ],
    "bestPractices": [
      "Use low patternAlpha values (10 - 25) for subtle, premium film grain that does not overpower foreground typography",
      "Pair with dark container backgrounds for optimal contrast with film grain highlights",
      "Enable scanlines or vignette for retro hardware and CRT aesthetics"
    ]
  },
  "props": [
    {
      "name": "patternSize",
      "type": "number",
      "defaultValue": "250",
      "description": "Dimensions (width and height in px) of the offscreen noise generator pattern buffer tile."
    },
    {
      "name": "patternScaleX",
      "type": "number",
      "defaultValue": "1",
      "description": "Horizontal scaling factor applied to the noise grain texture."
    },
    {
      "name": "patternScaleY",
      "type": "number",
      "defaultValue": "1",
      "description": "Vertical scaling factor applied to the noise grain texture."
    },
    {
      "name": "patternRefreshInterval",
      "type": "number",
      "defaultValue": "1",
      "description": "Frame interval divider governing pattern shifts (e.g. 1 updates every single frame)."
    },
    {
      "name": "patternAlpha",
      "type": "number",
      "defaultValue": "15",
      "description": "Opacity alpha intensity of the grain noise particles (0 - 255)."
    },
    {
      "name": "mode",
      "type": "\"grain\" | \"static\" | \"dust\"",
      "defaultValue": "\"grain\"",
      "description": "Noise rendering algorithm: film grain, CRT static, or film dust."
    },
    {
      "name": "animated",
      "type": "boolean",
      "defaultValue": "true",
      "description": "Whether the noise texture continuously shifts and jitters over time."
    },
    {
      "name": "fps",
      "type": "number",
      "defaultValue": "30",
      "description": "Target render frame rate throttle cap."
    },
    {
      "name": "vignette",
      "type": "boolean",
      "defaultValue": "false",
      "description": "Enables cinematic radial gradient vignette darkening around canvas boundaries."
    },
    {
      "name": "vignetteDarkness",
      "type": "number",
      "defaultValue": "0.6",
      "description": "Darkness multiplier factor for the radial vignette edge falloff."
    },
    {
      "name": "scanlines",
      "type": "boolean",
      "defaultValue": "false",
      "description": "Enables retro CRT monitor horizontal scanline overlay."
    },
    {
      "name": "scanlineDensity",
      "type": "number",
      "defaultValue": "3",
      "description": "Pixel pitch spacing between CRT scanline raster bands."
    },
    {
      "name": "scanlineOpacity",
      "type": "number",
      "defaultValue": "0.1",
      "description": "Opacity intensity of the CRT scanline raster bands."
    },
    {
      "name": "blendMode",
      "type": "CSSProperties['mixBlendMode']",
      "defaultValue": "\"normal\"",
      "description": "CSS mix-blend-mode applied to the noise canvas element (e.g. 'overlay', 'soft-light')."
    },
    {
      "name": "fullScreen",
      "type": "boolean",
      "defaultValue": "false",
      "description": "Whether the noise container fixes to the entire viewport (100vw x 100vh)."
    },
    {
      "name": "className",
      "type": "string",
      "defaultValue": "undefined",
      "description": "Additional CSS classes passed to the outer wrapper container."
    },
    {
      "name": "canvasClassName",
      "type": "string",
      "defaultValue": "undefined",
      "description": "Additional CSS classes passed directly to the underlying canvas element."
    },
    {
      "name": "canvasRef",
      "type": "React.Ref<HTMLCanvasElement>",
      "description": "Ref to the internal canvas element."
    },
    {
      "name": "children",
      "type": "ReactNode",
      "description": "Content rendered above the noise layer."
    }
  ],
  "usage": "import { Noise } from \"@/components/ui/noise\";\n\nexport function NoiseDemo() {\n  return (\n    <Noise patternAlpha={18} vignette className=\"h-80 w-full bg-zinc-950\">\n      <h2 className=\"p-8 text-white\">Film grain</h2>\n    </Noise>\n  );\n}",
  "files": [
    {
      "name": "noise.tsx",
      "path": "registry/ui/noise.tsx",
      "code": "\"use client\";\n\nimport React, {\n  forwardRef,\n  useCallback,\n  useEffect,\n  useImperativeHandle,\n  useRef,\n  useState,\n  type CSSProperties,\n  type HTMLAttributes,\n  type ReactNode,\n} from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nexport type NoiseMode = \"grain\" | \"static\" | \"dust\";\n\nexport interface NoiseProps extends HTMLAttributes<HTMLDivElement> {\n  patternSize?: number;\n  patternScaleX?: number;\n  patternScaleY?: number;\n  patternRefreshInterval?: number;\n  patternAlpha?: number;\n  mode?: NoiseMode;\n  animated?: boolean;\n  fps?: number;\n  vignette?: boolean;\n  vignetteDarkness?: number;\n  scanlines?: boolean;\n  scanlineDensity?: number;\n  scanlineOpacity?: number;\n  blendMode?: CSSProperties[\"mixBlendMode\"];\n  fullScreen?: boolean;\n  canvasClassName?: string;\n  canvasRef?: React.Ref<HTMLCanvasElement>;\n  children?: ReactNode;\n}\n\nconst createPatternFrame = (\n  size: number,\n  alpha: number,\n  mode: NoiseMode,\n  frameIdx: number,\n): HTMLCanvasElement => {\n  const pCanvas = document.createElement(\"canvas\");\n  pCanvas.width = size;\n  pCanvas.height = size;\n  const pCtx = pCanvas.getContext(\"2d\", { alpha: true });\n  if (!pCtx) return pCanvas;\n\n  const imgData = pCtx.createImageData(size, size);\n  const buf32 = new Uint32Array(imgData.data.buffer);\n  const total = size * size;\n\n  for (let i = 0; i < total; i++) {\n    const y = (i / size) | 0;\n\n    let v = 0;\n    let a = alpha;\n\n    if (mode === \"static\") {\n      const scan = (y + frameIdx * 3) % 8 === 0 ? 55 : 0;\n      v = Math.min(255, ((Math.random() * 256) | 0) + scan);\n    } else if (mode === \"dust\") {\n      const isDust = Math.random() > 0.994;\n      const isFleck = Math.random() > 0.999;\n      if (isDust) {\n        v = isFleck ? 255 : (Math.random() * 200 + 55) | 0;\n        a = Math.min(255, alpha * 5);\n      } else {\n        v = (Math.random() * 90) | 0;\n        a = (alpha * 0.35) | 0;\n      }\n    } else {\n      v = (Math.random() * 256) | 0;\n    }\n\n    buf32[i] = (a << 24) | (v << 16) | (v << 8) | v;\n  }\n\n  pCtx.putImageData(imgData, 0, 0);\n  return pCanvas;\n};\n\nexport const Noise = forwardRef<HTMLDivElement, NoiseProps>(\n  (\n    {\n      patternSize = 250,\n      patternScaleX = 1,\n      patternScaleY = 1,\n      patternRefreshInterval = 1,\n      patternAlpha = 15,\n      mode = \"grain\",\n      animated = true,\n      fps = 30,\n      vignette = false,\n      vignetteDarkness = 0.6,\n      scanlines = false,\n      scanlineDensity = 3,\n      scanlineOpacity = 0.1,\n      blendMode = \"normal\",\n      fullScreen = false,\n      canvasClassName,\n      canvasRef: externalCanvasRef,\n      className,\n      style,\n      children,\n      ...props\n    },\n    ref,\n  ) => {\n    const containerRef = useRef<HTMLDivElement | null>(null);\n    const internalCanvasRef = useRef<HTMLCanvasElement | null>(null);\n    const patternsRef = useRef<HTMLCanvasElement[]>([]);\n\n    const [isVisible, setIsVisible] = useState(true);\n    const [isPageActive, setIsPageActive] = useState(true);\n    const [reducedMotion, setReducedMotion] = useState(false);\n\n    useImperativeHandle(ref, () => containerRef.current as HTMLDivElement);\n\n    const setCanvasRefs = useCallback(\n      (node: HTMLCanvasElement | null) => {\n        internalCanvasRef.current = node;\n        if (typeof externalCanvasRef === \"function\") {\n          externalCanvasRef(node);\n        } else if (externalCanvasRef && \"current\" in externalCanvasRef) {\n          (\n            externalCanvasRef as React.MutableRefObject<HTMLCanvasElement | null>\n          ).current = node;\n        }\n      },\n      [externalCanvasRef],\n    );\n\n    useEffect(() => {\n      const media = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n      setReducedMotion(media.matches);\n      const listener = (e: MediaQueryListEvent) => setReducedMotion(e.matches);\n      media.addEventListener(\"change\", listener);\n      return () => media.removeEventListener(\"change\", listener);\n    }, []);\n\n    useEffect(() => {\n      const container = containerRef.current;\n      if (!container) return;\n\n      const observer = new IntersectionObserver(\n        (entries) => {\n          const [entry] = entries;\n          setIsVisible(entry.isIntersecting);\n        },\n        { threshold: 0 },\n      );\n\n      observer.observe(container);\n\n      const handleVisibility = () => {\n        setIsPageActive(!document.hidden);\n      };\n      document.addEventListener(\"visibilitychange\", handleVisibility);\n\n      return () => {\n        observer.disconnect();\n        document.removeEventListener(\"visibilitychange\", handleVisibility);\n      };\n    }, []);\n\n    useEffect(() => {\n      const container = containerRef.current;\n      const canvas = internalCanvasRef.current;\n      if (!container || !canvas) return;\n\n      const updateSize = () => {\n        const rect = container.getBoundingClientRect();\n        const dpr = Math.min(window.devicePixelRatio || 1, 2);\n        const displayWidth = Math.max(1, Math.round(rect.width));\n        const displayHeight = Math.max(1, Math.round(rect.height));\n\n        const targetW = displayWidth * dpr;\n        const targetH = displayHeight * dpr;\n\n        if (canvas.width !== targetW || canvas.height !== targetH) {\n          canvas.width = targetW;\n          canvas.height = targetH;\n          canvas.style.width = `${displayWidth}px`;\n          canvas.style.height = `${displayHeight}px`;\n        }\n      };\n\n      updateSize();\n      const observer = new ResizeObserver(updateSize);\n      observer.observe(container);\n\n      return () => {\n        observer.disconnect();\n      };\n    }, [fullScreen]);\n\n    useEffect(() => {\n      const frameCount = 8;\n      const frames: HTMLCanvasElement[] = [];\n\n      for (let i = 0; i < frameCount; i++) {\n        frames.push(createPatternFrame(patternSize, patternAlpha, mode, i));\n      }\n\n      patternsRef.current = frames;\n    }, [patternSize, patternAlpha, mode]);\n\n    useEffect(() => {\n      let animationId: number;\n      let frameCount = 0;\n      let cacheIdx = 0;\n      let lastTime = performance.now();\n      const targetInterval = fps > 0 ? 1000 / fps : 1000 / 60;\n\n      const render = (now: number) => {\n        animationId = window.requestAnimationFrame(render);\n\n        if (!isVisible || !isPageActive) return;\n\n        if (now - lastTime < targetInterval) return;\n        lastTime = now;\n\n        frameCount++;\n        if (\n          patternRefreshInterval > 1 &&\n          frameCount % patternRefreshInterval !== 0\n        ) {\n          return;\n        }\n\n        const canvas = internalCanvasRef.current;\n        if (!canvas) return;\n\n        const ctx = canvas.getContext(\"2d\", { alpha: true });\n        if (!ctx) return;\n\n        const w = canvas.width;\n        const h = canvas.height;\n        if (w === 0 || h === 0) return;\n\n        ctx.clearRect(0, 0, w, h);\n\n        const frames = patternsRef.current;\n        if (frames.length > 0) {\n          if (animated && !reducedMotion) {\n            cacheIdx = (cacheIdx + 1) % frames.length;\n          }\n          const currentFrame = frames[cacheIdx];\n          const pattern = ctx.createPattern(currentFrame, \"repeat\");\n          if (pattern) {\n            const matrix = new DOMMatrix();\n            if (animated && !reducedMotion) {\n              const jx = (Math.random() * patternSize) | 0;\n              const jy = (Math.random() * patternSize) | 0;\n              matrix.translateSelf(jx, jy);\n            }\n            matrix.scaleSelf(patternScaleX, patternScaleY);\n            pattern.setTransform(matrix);\n            ctx.fillStyle = pattern;\n            ctx.fillRect(0, 0, w, h);\n          }\n        }\n      };\n\n      animationId = window.requestAnimationFrame(render);\n\n      return () => {\n        window.cancelAnimationFrame(animationId);\n      };\n    }, [\n      patternSize,\n      patternScaleX,\n      patternScaleY,\n      patternRefreshInterval,\n      animated,\n      fps,\n      isVisible,\n      isPageActive,\n      reducedMotion,\n    ]);\n\n    return (\n      <div\n        ref={containerRef}\n        data-slot=\"noise\"\n        className={cn(\n          \"relative overflow-hidden\",\n          fullScreen ? \"fixed inset-0 h-screen w-screen z-0\" : \"w-full h-full\",\n          className,\n        )}\n        style={style}\n        {...props}\n      >\n        <canvas\n          ref={setCanvasRefs}\n          data-slot=\"noise-canvas\"\n          className={cn(\n            \"pointer-events-none absolute inset-0 h-full w-full\",\n            canvasClassName,\n          )}\n          style={{\n            imageRendering: \"pixelated\",\n            mixBlendMode: blendMode,\n          }}\n        />\n\n        {vignette && (\n          <div\n            data-slot=\"noise-vignette\"\n            className=\"pointer-events-none absolute inset-0 z-2\"\n            style={{\n              background: `radial-gradient(ellipse at center, transparent 35%, rgba(0, 0, 0, ${vignetteDarkness}) 100%)`,\n            }}\n          />\n        )}\n\n        {scanlines && (\n          <div\n            data-slot=\"noise-scanlines\"\n            className=\"pointer-events-none absolute inset-0 z-3\"\n            style={{\n              background: `repeating-linear-gradient(to bottom, transparent 0px, transparent ${scanlineDensity}px, rgba(0, 0, 0, ${scanlineOpacity}) ${scanlineDensity}px, rgba(0, 0, 0, ${scanlineOpacity}) ${scanlineDensity * 2}px)`,\n            }}\n          />\n        )}\n\n        {children && <div className=\"relative z-10\">{children}</div>}\n      </div>\n    );\n  },\n);\n\nNoise.displayName = \"Noise\";\n\nexport default Noise;\n",
      "content": "\"use client\";\n\nimport React, {\n  forwardRef,\n  useCallback,\n  useEffect,\n  useImperativeHandle,\n  useRef,\n  useState,\n  type CSSProperties,\n  type HTMLAttributes,\n  type ReactNode,\n} from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nexport type NoiseMode = \"grain\" | \"static\" | \"dust\";\n\nexport interface NoiseProps extends HTMLAttributes<HTMLDivElement> {\n  patternSize?: number;\n  patternScaleX?: number;\n  patternScaleY?: number;\n  patternRefreshInterval?: number;\n  patternAlpha?: number;\n  mode?: NoiseMode;\n  animated?: boolean;\n  fps?: number;\n  vignette?: boolean;\n  vignetteDarkness?: number;\n  scanlines?: boolean;\n  scanlineDensity?: number;\n  scanlineOpacity?: number;\n  blendMode?: CSSProperties[\"mixBlendMode\"];\n  fullScreen?: boolean;\n  canvasClassName?: string;\n  canvasRef?: React.Ref<HTMLCanvasElement>;\n  children?: ReactNode;\n}\n\nconst createPatternFrame = (\n  size: number,\n  alpha: number,\n  mode: NoiseMode,\n  frameIdx: number,\n): HTMLCanvasElement => {\n  const pCanvas = document.createElement(\"canvas\");\n  pCanvas.width = size;\n  pCanvas.height = size;\n  const pCtx = pCanvas.getContext(\"2d\", { alpha: true });\n  if (!pCtx) return pCanvas;\n\n  const imgData = pCtx.createImageData(size, size);\n  const buf32 = new Uint32Array(imgData.data.buffer);\n  const total = size * size;\n\n  for (let i = 0; i < total; i++) {\n    const y = (i / size) | 0;\n\n    let v = 0;\n    let a = alpha;\n\n    if (mode === \"static\") {\n      const scan = (y + frameIdx * 3) % 8 === 0 ? 55 : 0;\n      v = Math.min(255, ((Math.random() * 256) | 0) + scan);\n    } else if (mode === \"dust\") {\n      const isDust = Math.random() > 0.994;\n      const isFleck = Math.random() > 0.999;\n      if (isDust) {\n        v = isFleck ? 255 : (Math.random() * 200 + 55) | 0;\n        a = Math.min(255, alpha * 5);\n      } else {\n        v = (Math.random() * 90) | 0;\n        a = (alpha * 0.35) | 0;\n      }\n    } else {\n      v = (Math.random() * 256) | 0;\n    }\n\n    buf32[i] = (a << 24) | (v << 16) | (v << 8) | v;\n  }\n\n  pCtx.putImageData(imgData, 0, 0);\n  return pCanvas;\n};\n\nexport const Noise = forwardRef<HTMLDivElement, NoiseProps>(\n  (\n    {\n      patternSize = 250,\n      patternScaleX = 1,\n      patternScaleY = 1,\n      patternRefreshInterval = 1,\n      patternAlpha = 15,\n      mode = \"grain\",\n      animated = true,\n      fps = 30,\n      vignette = false,\n      vignetteDarkness = 0.6,\n      scanlines = false,\n      scanlineDensity = 3,\n      scanlineOpacity = 0.1,\n      blendMode = \"normal\",\n      fullScreen = false,\n      canvasClassName,\n      canvasRef: externalCanvasRef,\n      className,\n      style,\n      children,\n      ...props\n    },\n    ref,\n  ) => {\n    const containerRef = useRef<HTMLDivElement | null>(null);\n    const internalCanvasRef = useRef<HTMLCanvasElement | null>(null);\n    const patternsRef = useRef<HTMLCanvasElement[]>([]);\n\n    const [isVisible, setIsVisible] = useState(true);\n    const [isPageActive, setIsPageActive] = useState(true);\n    const [reducedMotion, setReducedMotion] = useState(false);\n\n    useImperativeHandle(ref, () => containerRef.current as HTMLDivElement);\n\n    const setCanvasRefs = useCallback(\n      (node: HTMLCanvasElement | null) => {\n        internalCanvasRef.current = node;\n        if (typeof externalCanvasRef === \"function\") {\n          externalCanvasRef(node);\n        } else if (externalCanvasRef && \"current\" in externalCanvasRef) {\n          (\n            externalCanvasRef as React.MutableRefObject<HTMLCanvasElement | null>\n          ).current = node;\n        }\n      },\n      [externalCanvasRef],\n    );\n\n    useEffect(() => {\n      const media = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n      setReducedMotion(media.matches);\n      const listener = (e: MediaQueryListEvent) => setReducedMotion(e.matches);\n      media.addEventListener(\"change\", listener);\n      return () => media.removeEventListener(\"change\", listener);\n    }, []);\n\n    useEffect(() => {\n      const container = containerRef.current;\n      if (!container) return;\n\n      const observer = new IntersectionObserver(\n        (entries) => {\n          const [entry] = entries;\n          setIsVisible(entry.isIntersecting);\n        },\n        { threshold: 0 },\n      );\n\n      observer.observe(container);\n\n      const handleVisibility = () => {\n        setIsPageActive(!document.hidden);\n      };\n      document.addEventListener(\"visibilitychange\", handleVisibility);\n\n      return () => {\n        observer.disconnect();\n        document.removeEventListener(\"visibilitychange\", handleVisibility);\n      };\n    }, []);\n\n    useEffect(() => {\n      const container = containerRef.current;\n      const canvas = internalCanvasRef.current;\n      if (!container || !canvas) return;\n\n      const updateSize = () => {\n        const rect = container.getBoundingClientRect();\n        const dpr = Math.min(window.devicePixelRatio || 1, 2);\n        const displayWidth = Math.max(1, Math.round(rect.width));\n        const displayHeight = Math.max(1, Math.round(rect.height));\n\n        const targetW = displayWidth * dpr;\n        const targetH = displayHeight * dpr;\n\n        if (canvas.width !== targetW || canvas.height !== targetH) {\n          canvas.width = targetW;\n          canvas.height = targetH;\n          canvas.style.width = `${displayWidth}px`;\n          canvas.style.height = `${displayHeight}px`;\n        }\n      };\n\n      updateSize();\n      const observer = new ResizeObserver(updateSize);\n      observer.observe(container);\n\n      return () => {\n        observer.disconnect();\n      };\n    }, [fullScreen]);\n\n    useEffect(() => {\n      const frameCount = 8;\n      const frames: HTMLCanvasElement[] = [];\n\n      for (let i = 0; i < frameCount; i++) {\n        frames.push(createPatternFrame(patternSize, patternAlpha, mode, i));\n      }\n\n      patternsRef.current = frames;\n    }, [patternSize, patternAlpha, mode]);\n\n    useEffect(() => {\n      let animationId: number;\n      let frameCount = 0;\n      let cacheIdx = 0;\n      let lastTime = performance.now();\n      const targetInterval = fps > 0 ? 1000 / fps : 1000 / 60;\n\n      const render = (now: number) => {\n        animationId = window.requestAnimationFrame(render);\n\n        if (!isVisible || !isPageActive) return;\n\n        if (now - lastTime < targetInterval) return;\n        lastTime = now;\n\n        frameCount++;\n        if (\n          patternRefreshInterval > 1 &&\n          frameCount % patternRefreshInterval !== 0\n        ) {\n          return;\n        }\n\n        const canvas = internalCanvasRef.current;\n        if (!canvas) return;\n\n        const ctx = canvas.getContext(\"2d\", { alpha: true });\n        if (!ctx) return;\n\n        const w = canvas.width;\n        const h = canvas.height;\n        if (w === 0 || h === 0) return;\n\n        ctx.clearRect(0, 0, w, h);\n\n        const frames = patternsRef.current;\n        if (frames.length > 0) {\n          if (animated && !reducedMotion) {\n            cacheIdx = (cacheIdx + 1) % frames.length;\n          }\n          const currentFrame = frames[cacheIdx];\n          const pattern = ctx.createPattern(currentFrame, \"repeat\");\n          if (pattern) {\n            const matrix = new DOMMatrix();\n            if (animated && !reducedMotion) {\n              const jx = (Math.random() * patternSize) | 0;\n              const jy = (Math.random() * patternSize) | 0;\n              matrix.translateSelf(jx, jy);\n            }\n            matrix.scaleSelf(patternScaleX, patternScaleY);\n            pattern.setTransform(matrix);\n            ctx.fillStyle = pattern;\n            ctx.fillRect(0, 0, w, h);\n          }\n        }\n      };\n\n      animationId = window.requestAnimationFrame(render);\n\n      return () => {\n        window.cancelAnimationFrame(animationId);\n      };\n    }, [\n      patternSize,\n      patternScaleX,\n      patternScaleY,\n      patternRefreshInterval,\n      animated,\n      fps,\n      isVisible,\n      isPageActive,\n      reducedMotion,\n    ]);\n\n    return (\n      <div\n        ref={containerRef}\n        data-slot=\"noise\"\n        className={cn(\n          \"relative overflow-hidden\",\n          fullScreen ? \"fixed inset-0 h-screen w-screen z-0\" : \"w-full h-full\",\n          className,\n        )}\n        style={style}\n        {...props}\n      >\n        <canvas\n          ref={setCanvasRefs}\n          data-slot=\"noise-canvas\"\n          className={cn(\n            \"pointer-events-none absolute inset-0 h-full w-full\",\n            canvasClassName,\n          )}\n          style={{\n            imageRendering: \"pixelated\",\n            mixBlendMode: blendMode,\n          }}\n        />\n\n        {vignette && (\n          <div\n            data-slot=\"noise-vignette\"\n            className=\"pointer-events-none absolute inset-0 z-2\"\n            style={{\n              background: `radial-gradient(ellipse at center, transparent 35%, rgba(0, 0, 0, ${vignetteDarkness}) 100%)`,\n            }}\n          />\n        )}\n\n        {scanlines && (\n          <div\n            data-slot=\"noise-scanlines\"\n            className=\"pointer-events-none absolute inset-0 z-3\"\n            style={{\n              background: `repeating-linear-gradient(to bottom, transparent 0px, transparent ${scanlineDensity}px, rgba(0, 0, 0, ${scanlineOpacity}) ${scanlineDensity}px, rgba(0, 0, 0, ${scanlineOpacity}) ${scanlineDensity * 2}px)`,\n            }}\n          />\n        )}\n\n        {children && <div className=\"relative z-10\">{children}</div>}\n      </div>\n    );\n  },\n);\n\nNoise.displayName = \"Noise\";\n\nexport default Noise;\n",
      "type": "registry:ui",
      "target": "@ui/noise.tsx"
    }
  ],
  "type": "registry:ui",
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "title": "Noise Background"
}
