{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "PixelBlast-TS-CSS",
	"title": "PixelBlast",
	"description": "Exploding pixel particle bursts with optional liquid postprocessing.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:file",
			"path": "PixelBlast.css",
			"target": "@components/PixelBlast.css",
			"content": ".pixel-blast-container {\n  width: 100%;\n  height: 100%;\n  position: relative;\n  overflow: hidden;\n}\n"
		},
		{
			"type": "registry:component",
			"path": "PixelBlast.tsx",
			"content": "import { Effect, EffectComposer, EffectPass, RenderPass } from 'postprocessing';\nimport React, { useEffect, useRef } from 'react';\nimport * as THREE from 'three';\nimport './PixelBlast.css';\n\ntype PixelBlastVariant = 'square' | 'circle' | 'triangle' | 'diamond';\n\ninterface TouchPoint {\n  x: number;\n  y: number;\n  vx: number;\n  vy: number;\n  force: number;\n  age: number;\n}\n\ninterface TouchTexture {\n  canvas: HTMLCanvasElement;\n  texture: THREE.Texture;\n  addTouch: (norm: { x: number; y: number }) => void;\n  update: () => void;\n  radiusScale: number;\n  size: number;\n}\n\ninterface ReinitConfig {\n  antialias: boolean;\n  liquid: boolean;\n  noiseAmount: number;\n}\n\ntype PixelBlastProps = {\n  variant?: PixelBlastVariant;\n  pixelSize?: number;\n  color?: string;\n  className?: string;\n  style?: React.CSSProperties;\n  antialias?: boolean;\n  patternScale?: number;\n  patternDensity?: number;\n  liquid?: boolean;\n  liquidStrength?: number;\n  liquidRadius?: number;\n  pixelSizeJitter?: number;\n  enableRipples?: boolean;\n  rippleIntensityScale?: number;\n  rippleThickness?: number;\n  rippleSpeed?: number;\n  liquidWobbleSpeed?: number;\n  autoPauseOffscreen?: boolean;\n  speed?: number;\n  transparent?: boolean;\n  edgeFade?: number;\n  noiseAmount?: number;\n};\n\nconst createTouchTexture = (): TouchTexture => {\n  const size = 64;\n  const canvas = document.createElement('canvas');\n  canvas.width = size;\n  canvas.height = size;\n  const ctx = canvas.getContext('2d');\n  if (!ctx) throw new Error('2D context not available');\n  ctx.fillStyle = 'black';\n  ctx.fillRect(0, 0, canvas.width, canvas.height);\n  const texture = new THREE.Texture(canvas);\n  texture.minFilter = THREE.LinearFilter;\n  texture.magFilter = THREE.LinearFilter;\n  texture.generateMipmaps = false;\n  const trail: TouchPoint[] = [];\n  let last: { x: number; y: number } | null = null;\n  const maxAge = 64;\n  let radius = 0.1 * size;\n  const speed = 1 / maxAge;\n  const clear = () => {\n    ctx.fillStyle = 'black';\n    ctx.fillRect(0, 0, canvas.width, canvas.height);\n  };\n  const drawPoint = (p: TouchPoint) => {\n    const pos = { x: p.x * size, y: (1 - p.y) * size };\n    let intensity = 1;\n    const easeOutSine = (t: number) => Math.sin((t * Math.PI) / 2);\n    const easeOutQuad = (t: number) => -t * (t - 2);\n    if (p.age < maxAge * 0.3) intensity = easeOutSine(p.age / (maxAge * 0.3));\n    else intensity = easeOutQuad(1 - (p.age - maxAge * 0.3) / (maxAge * 0.7)) || 0;\n    intensity *= p.force;\n    const color = `${((p.vx + 1) / 2) * 255}, ${((p.vy + 1) / 2) * 255}, ${intensity * 255}`;\n    const offset = size * 5;\n    ctx.shadowOffsetX = offset;\n    ctx.shadowOffsetY = offset;\n    ctx.shadowBlur = radius;\n    ctx.shadowColor = `rgba(${color},${0.22 * intensity})`;\n    ctx.beginPath();\n    ctx.fillStyle = 'rgba(255,0,0,1)';\n    ctx.arc(pos.x - offset, pos.y - offset, radius, 0, Math.PI * 2);\n    ctx.fill();\n  };\n  const addTouch = (norm: { x: number; y: number }) => {\n    let force = 0;\n    let vx = 0;\n    let vy = 0;\n    if (last) {\n      const dx = norm.x - last.x;\n      const dy = norm.y - last.y;\n      if (dx === 0 && dy === 0) return;\n      const dd = dx * dx + dy * dy;\n      const d = Math.sqrt(dd);\n      vx = dx / (d || 1);\n      vy = dy / (d || 1);\n      force = Math.min(dd * 10000, 1);\n    }\n    last = { x: norm.x, y: norm.y };\n    trail.push({ x: norm.x, y: norm.y, age: 0, force, vx, vy });\n  };\n  const update = () => {\n    clear();\n    for (let i = trail.length - 1; i >= 0; i--) {\n      const point = trail[i];\n      const f = point.force * speed * (1 - point.age / maxAge);\n      point.x += point.vx * f;\n      point.y += point.vy * f;\n      point.age++;\n      if (point.age > maxAge) trail.splice(i, 1);\n    }\n    for (let i = 0; i < trail.length; i++) drawPoint(trail[i]);\n    texture.needsUpdate = true;\n  };\n  return {\n    canvas,\n    texture,\n    addTouch,\n    update,\n    set radiusScale(v: number) {\n      radius = 0.1 * size * v;\n    },\n    get radiusScale() {\n      return radius / (0.1 * size);\n    },\n    size\n  };\n};\n\nconst createLiquidEffect = (texture: THREE.Texture, opts?: { strength?: number; freq?: number }) => {\n  const fragment = `\n    uniform sampler2D uTexture;\n    uniform float uStrength;\n    uniform float uTime;\n    uniform float uFreq;\n\n    void mainUv(inout vec2 uv) {\n      vec4 tex = texture2D(uTexture, uv);\n      float vx = tex.r * 2.0 - 1.0;\n      float vy = tex.g * 2.0 - 1.0;\n      float intensity = tex.b;\n\n      float wave = 0.5 + 0.5 * sin(uTime * uFreq + intensity * 6.2831853);\n\n      float amt = uStrength * intensity * wave;\n\n      uv += vec2(vx, vy) * amt;\n    }\n    `;\n  return new Effect('LiquidEffect', fragment, {\n    uniforms: new Map<string, THREE.Uniform>([\n      ['uTexture', new THREE.Uniform(texture)],\n      ['uStrength', new THREE.Uniform(opts?.strength ?? 0.025)],\n      ['uTime', new THREE.Uniform(0)],\n      ['uFreq', new THREE.Uniform(opts?.freq ?? 4.5)]\n    ])\n  });\n};\n\nconst SHAPE_MAP: Record<PixelBlastVariant, number> = {\n  square: 0,\n  circle: 1,\n  triangle: 2,\n  diamond: 3\n};\n\nconst VERTEX_SRC = `\nvoid main() {\n  gl_Position = vec4(position, 1.0);\n}\n`;\n\nconst FRAGMENT_SRC = `\nprecision highp float;\n\nuniform vec3  uColor;\nuniform vec2  uResolution;\nuniform float uTime;\nuniform float uPixelSize;\nuniform float uScale;\nuniform float uDensity;\nuniform float uPixelJitter;\nuniform int   uEnableRipples;\nuniform float uRippleSpeed;\nuniform float uRippleThickness;\nuniform float uRippleIntensity;\nuniform float uEdgeFade;\n\nuniform int   uShapeType;\nconst int SHAPE_SQUARE   = 0;\nconst int SHAPE_CIRCLE   = 1;\nconst int SHAPE_TRIANGLE = 2;\nconst int SHAPE_DIAMOND  = 3;\n\nconst int   MAX_CLICKS = 10;\n\nuniform vec2  uClickPos  [MAX_CLICKS];\nuniform float uClickTimes[MAX_CLICKS];\n\nout vec4 fragColor;\n\nfloat Bayer2(vec2 a) {\n  a = floor(a);\n  return fract(a.x / 2. + a.y * a.y * .75);\n}\n#define Bayer4(a) (Bayer2(.5*(a))*0.25 + Bayer2(a))\n#define Bayer8(a) (Bayer4(.5*(a))*0.25 + Bayer2(a))\n\n#define FBM_OCTAVES     5\n#define FBM_LACUNARITY  1.25\n#define FBM_GAIN        1.0\n\nfloat hash11(float n){ return fract(sin(n)*43758.5453); }\n\nfloat vnoise(vec3 p){\n  vec3 ip = floor(p);\n  vec3 fp = fract(p);\n  float n000 = hash11(dot(ip + vec3(0.0,0.0,0.0), vec3(1.0,57.0,113.0)));\n  float n100 = hash11(dot(ip + vec3(1.0,0.0,0.0), vec3(1.0,57.0,113.0)));\n  float n010 = hash11(dot(ip + vec3(0.0,1.0,0.0), vec3(1.0,57.0,113.0)));\n  float n110 = hash11(dot(ip + vec3(1.0,1.0,0.0), vec3(1.0,57.0,113.0)));\n  float n001 = hash11(dot(ip + vec3(0.0,0.0,1.0), vec3(1.0,57.0,113.0)));\n  float n101 = hash11(dot(ip + vec3(1.0,0.0,1.0), vec3(1.0,57.0,113.0)));\n  float n011 = hash11(dot(ip + vec3(0.0,1.0,1.0), vec3(1.0,57.0,113.0)));\n  float n111 = hash11(dot(ip + vec3(1.0,1.0,1.0), vec3(1.0,57.0,113.0)));\n  vec3 w = fp*fp*fp*(fp*(fp*6.0-15.0)+10.0);\n  float x00 = mix(n000, n100, w.x);\n  float x10 = mix(n010, n110, w.x);\n  float x01 = mix(n001, n101, w.x);\n  float x11 = mix(n011, n111, w.x);\n  float y0  = mix(x00, x10, w.y);\n  float y1  = mix(x01, x11, w.y);\n  return mix(y0, y1, w.z) * 2.0 - 1.0;\n}\n\nfloat fbm2(vec2 uv, float t){\n  vec3 p = vec3(uv * uScale, t);\n  float amp = 1.0;\n  float freq = 1.0;\n  float sum = 1.0;\n  for (int i = 0; i < FBM_OCTAVES; ++i){\n    sum  += amp * vnoise(p * freq);\n    freq *= FBM_LACUNARITY;\n    amp  *= FBM_GAIN;\n  }\n  return sum * 0.5 + 0.5;\n}\n\nfloat maskCircle(vec2 p, float cov){\n  float r = sqrt(cov) * .25;\n  float d = length(p - 0.5) - r;\n  float aa = 0.5 * fwidth(d);\n  return cov * (1.0 - smoothstep(-aa, aa, d * 2.0));\n}\n\nfloat maskTriangle(vec2 p, vec2 id, float cov){\n  bool flip = mod(id.x + id.y, 2.0) > 0.5;\n  if (flip) p.x = 1.0 - p.x;\n  float r = sqrt(cov);\n  float d  = p.y - r*(1.0 - p.x);\n  float aa = fwidth(d);\n  return cov * clamp(0.5 - d/aa, 0.0, 1.0);\n}\n\nfloat maskDiamond(vec2 p, float cov){\n  float r = sqrt(cov) * 0.564;\n  return step(abs(p.x - 0.49) + abs(p.y - 0.49), r);\n}\n\nvoid main(){\n  float pixelSize = uPixelSize;\n  vec2 fragCoord = gl_FragCoord.xy - uResolution * .5;\n  float aspectRatio = uResolution.x / uResolution.y;\n\n  vec2 pixelId = floor(fragCoord / pixelSize);\n  vec2 pixelUV = fract(fragCoord / pixelSize);\n\n  float cellPixelSize = 8.0 * pixelSize;\n  vec2 cellId = floor(fragCoord / cellPixelSize);\n  vec2 cellCoord = cellId * cellPixelSize;\n  vec2 uv = cellCoord / uResolution * vec2(aspectRatio, 1.0);\n\n  float base = fbm2(uv, uTime * 0.05);\n  base = base * 0.5 - 0.65;\n\n  float feed = base + (uDensity - 0.5) * 0.3;\n\n  float speed     = uRippleSpeed;\n  float thickness = uRippleThickness;\n  const float dampT     = 1.0;\n  const float dampR     = 10.0;\n\n  if (uEnableRipples == 1) {\n    for (int i = 0; i < MAX_CLICKS; ++i){\n      vec2 pos = uClickPos[i];\n      if (pos.x < 0.0) continue;\n      float cellPixelSize = 8.0 * pixelSize;\n      vec2 cuv = (((pos - uResolution * .5 - cellPixelSize * .5) / (uResolution))) * vec2(aspectRatio, 1.0);\n      float t = max(uTime - uClickTimes[i], 0.0);\n      float r = distance(uv, cuv);\n      float waveR = speed * t;\n      float ring  = exp(-pow((r - waveR) / thickness, 2.0));\n      float atten = exp(-dampT * t) * exp(-dampR * r);\n      feed = max(feed, ring * atten * uRippleIntensity);\n    }\n  }\n\n  float bayer = Bayer8(fragCoord / uPixelSize) - 0.5;\n  float bw = step(0.5, feed + bayer);\n\n  float h = fract(sin(dot(floor(fragCoord / uPixelSize), vec2(127.1, 311.7))) * 43758.5453);\n  float jitterScale = 1.0 + (h - 0.5) * uPixelJitter;\n  float coverage = bw * jitterScale;\n  float M;\n  if      (uShapeType == SHAPE_CIRCLE)   M = maskCircle (pixelUV, coverage);\n  else if (uShapeType == SHAPE_TRIANGLE) M = maskTriangle(pixelUV, pixelId, coverage);\n  else if (uShapeType == SHAPE_DIAMOND)  M = maskDiamond(pixelUV, coverage);\n  else                                   M = coverage;\n\n  if (uEdgeFade > 0.0) {\n    vec2 norm = gl_FragCoord.xy / uResolution;\n    float edge = min(min(norm.x, norm.y), min(1.0 - norm.x, 1.0 - norm.y));\n    float fade = smoothstep(0.0, uEdgeFade, edge);\n    M *= fade;\n  }\n\n  vec3 color = uColor;\n\n  // sRGB gamma correction - convert linear to sRGB for accurate color output\n  vec3 srgbColor = mix(\n    color * 12.92,\n    1.055 * pow(color, vec3(1.0 / 2.4)) - 0.055,\n    step(0.0031308, color)\n  );\n\n  fragColor = vec4(srgbColor, M);\n}\n`;\n\nconst MAX_CLICKS = 10;\n\nconst PixelBlast: React.FC<PixelBlastProps> = ({\n  variant = 'square',\n  pixelSize = 3,\n  color = '#B497CF',\n  className,\n  style,\n  antialias = true,\n  patternScale = 2,\n  patternDensity = 1,\n  liquid = false,\n  liquidStrength = 0.1,\n  liquidRadius = 1,\n  pixelSizeJitter = 0,\n  enableRipples = true,\n  rippleIntensityScale = 1,\n  rippleThickness = 0.1,\n  rippleSpeed = 0.3,\n  liquidWobbleSpeed = 4.5,\n  autoPauseOffscreen = true,\n  speed = 0.5,\n  transparent = true,\n  edgeFade = 0.5,\n  noiseAmount = 0\n}) => {\n  const containerRef = useRef<HTMLDivElement | null>(null);\n  const visibilityRef = useRef({ visible: true });\n  const speedRef = useRef(speed);\n\n  const threeRef = useRef<{\n    renderer: THREE.WebGLRenderer;\n    scene: THREE.Scene;\n    camera: THREE.OrthographicCamera;\n    material: THREE.ShaderMaterial;\n    clock: THREE.Clock;\n    clickIx: number;\n    uniforms: {\n      uResolution: { value: THREE.Vector2 };\n      uTime: { value: number };\n      uColor: { value: THREE.Color };\n      uClickPos: { value: THREE.Vector2[] };\n      uClickTimes: { value: Float32Array };\n      uShapeType: { value: number };\n      uPixelSize: { value: number };\n      uScale: { value: number };\n      uDensity: { value: number };\n      uPixelJitter: { value: number };\n      uEnableRipples: { value: number };\n      uRippleSpeed: { value: number };\n      uRippleThickness: { value: number };\n      uRippleIntensity: { value: number };\n      uEdgeFade: { value: number };\n    };\n    resizeObserver?: ResizeObserver;\n    raf?: number;\n    quad?: THREE.Mesh<THREE.PlaneGeometry, THREE.ShaderMaterial>;\n    timeOffset?: number;\n    composer?: EffectComposer;\n    touch?: ReturnType<typeof createTouchTexture>;\n    liquidEffect?: Effect;\n  } | null>(null);\n  const prevConfigRef = useRef<ReinitConfig | null>(null);\n  useEffect(() => {\n    const container = containerRef.current;\n    if (!container) return;\n    speedRef.current = speed;\n    const needsReinitKeys: (keyof ReinitConfig)[] = ['antialias', 'liquid', 'noiseAmount'];\n    const cfg: ReinitConfig = { antialias, liquid, noiseAmount };\n    let mustReinit = false;\n    if (!threeRef.current) mustReinit = true;\n    else if (prevConfigRef.current) {\n      for (const k of needsReinitKeys)\n        if (prevConfigRef.current[k] !== cfg[k]) {\n          mustReinit = true;\n          break;\n        }\n    }\n    if (mustReinit) {\n      if (threeRef.current) {\n        const t = threeRef.current;\n        t.resizeObserver?.disconnect();\n        cancelAnimationFrame(t.raf!);\n        t.quad?.geometry.dispose();\n        t.material.dispose();\n        t.composer?.dispose();\n        t.renderer.dispose();\n        t.renderer.forceContextLoss();\n        if (t.renderer.domElement.parentElement === container) container.removeChild(t.renderer.domElement);\n        threeRef.current = null;\n      }\n      const canvas = document.createElement('canvas');\n      const renderer = new THREE.WebGLRenderer({\n        canvas,\n        antialias,\n        alpha: true,\n        powerPreference: 'high-performance'\n      });\n      renderer.domElement.style.width = '100%';\n      renderer.domElement.style.height = '100%';\n      renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));\n      container.appendChild(renderer.domElement);\n      if (transparent) renderer.setClearAlpha(0);\n      else renderer.setClearColor(0x000000, 1);\n      const uniforms = {\n        uResolution: { value: new THREE.Vector2(0, 0) },\n        uTime: { value: 0 },\n        uColor: { value: new THREE.Color(color) },\n        uClickPos: {\n          value: Array.from({ length: MAX_CLICKS }, () => new THREE.Vector2(-1, -1))\n        },\n        uClickTimes: { value: new Float32Array(MAX_CLICKS) },\n        uShapeType: { value: SHAPE_MAP[variant] ?? 0 },\n        uPixelSize: { value: pixelSize * renderer.getPixelRatio() },\n        uScale: { value: patternScale },\n        uDensity: { value: patternDensity },\n        uPixelJitter: { value: pixelSizeJitter },\n        uEnableRipples: { value: enableRipples ? 1 : 0 },\n        uRippleSpeed: { value: rippleSpeed },\n        uRippleThickness: { value: rippleThickness },\n        uRippleIntensity: { value: rippleIntensityScale },\n        uEdgeFade: { value: edgeFade }\n      };\n      const scene = new THREE.Scene();\n      const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);\n      const material = new THREE.ShaderMaterial({\n        vertexShader: VERTEX_SRC,\n        fragmentShader: FRAGMENT_SRC,\n        uniforms,\n        transparent: true,\n        depthTest: false,\n        depthWrite: false,\n        glslVersion: THREE.GLSL3\n      });\n\n      const quadGeom = new THREE.PlaneGeometry(2, 2);\n      const quad = new THREE.Mesh(quadGeom, material);\n      scene.add(quad);\n      const clock = new THREE.Clock();\n      const setSize = () => {\n        const w = container.clientWidth || 1;\n        const h = container.clientHeight || 1;\n        renderer.setSize(w, h, false);\n        uniforms.uResolution.value.set(renderer.domElement.width, renderer.domElement.height);\n        if (threeRef.current?.composer)\n          threeRef.current.composer.setSize(renderer.domElement.width, renderer.domElement.height);\n        uniforms.uPixelSize.value = pixelSize * renderer.getPixelRatio();\n      };\n      setSize();\n      const ro = new ResizeObserver(setSize);\n      ro.observe(container);\n      const randomFloat = (): number => {\n        if (typeof window !== 'undefined' && window.crypto?.getRandomValues) {\n          const u32 = new Uint32Array(1);\n          window.crypto.getRandomValues(u32);\n          return u32[0] / 0xffffffff;\n        }\n        return Math.random();\n      };\n      const timeOffset = randomFloat() * 1000;\n      let composer: EffectComposer | undefined;\n      let touch: ReturnType<typeof createTouchTexture> | undefined;\n      let liquidEffect: Effect | undefined;\n      if (liquid) {\n        touch = createTouchTexture();\n        touch.radiusScale = liquidRadius;\n        composer = new EffectComposer(renderer);\n        const renderPass = new RenderPass(scene, camera);\n        liquidEffect = createLiquidEffect(touch.texture, {\n          strength: liquidStrength,\n          freq: liquidWobbleSpeed\n        });\n        const effectPass = new EffectPass(camera, liquidEffect);\n        effectPass.renderToScreen = true;\n        composer.addPass(renderPass);\n        composer.addPass(effectPass);\n      }\n      if (noiseAmount > 0) {\n        if (!composer) {\n          composer = new EffectComposer(renderer);\n          composer.addPass(new RenderPass(scene, camera));\n        }\n        const noiseEffect = new Effect(\n          'NoiseEffect',\n          `uniform float uTime; uniform float uAmount; float hash(vec2 p){ return fract(sin(dot(p, vec2(127.1,311.7))) * 43758.5453);} void mainUv(inout vec2 uv){} void mainImage(const in vec4 inputColor,const in vec2 uv,out vec4 outputColor){ float n=hash(floor(uv*vec2(1920.0,1080.0))+floor(uTime*60.0)); float g=(n-0.5)*uAmount; outputColor=inputColor+vec4(vec3(g),0.0);} `,\n          {\n            uniforms: new Map<string, THREE.Uniform>([\n              ['uTime', new THREE.Uniform(0)],\n              ['uAmount', new THREE.Uniform(noiseAmount)]\n            ])\n          }\n        );\n        const noisePass = new EffectPass(camera, noiseEffect);\n        noisePass.renderToScreen = true;\n        if (composer && composer.passes.length > 0) {\n          composer.passes.forEach(p => {\n            const pass = p as { renderToScreen?: boolean };\n            pass.renderToScreen = false;\n          });\n        }\n        composer.addPass(noisePass);\n      }\n      if (composer) composer.setSize(renderer.domElement.width, renderer.domElement.height);\n      const mapToPixels = (e: PointerEvent) => {\n        const rect = renderer.domElement.getBoundingClientRect();\n        const scaleX = renderer.domElement.width / rect.width;\n        const scaleY = renderer.domElement.height / rect.height;\n        const fx = (e.clientX - rect.left) * scaleX;\n        const fy = (rect.height - (e.clientY - rect.top)) * scaleY;\n        return {\n          fx,\n          fy,\n          w: renderer.domElement.width,\n          h: renderer.domElement.height\n        };\n      };\n      const onPointerDown = (e: PointerEvent) => {\n        const { fx, fy } = mapToPixels(e);\n        const ix = threeRef.current?.clickIx ?? 0;\n        uniforms.uClickPos.value[ix].set(fx, fy);\n        uniforms.uClickTimes.value[ix] = uniforms.uTime.value;\n        if (threeRef.current) threeRef.current.clickIx = (ix + 1) % MAX_CLICKS;\n      };\n      const onPointerMove = (e: PointerEvent) => {\n        if (!touch) return;\n        const { fx, fy, w, h } = mapToPixels(e);\n        touch.addTouch({ x: fx / w, y: fy / h });\n      };\n      renderer.domElement.addEventListener('pointerdown', onPointerDown, {\n        passive: true\n      });\n      renderer.domElement.addEventListener('pointermove', onPointerMove, {\n        passive: true\n      });\n      let raf = 0;\n      const animate = () => {\n        if (autoPauseOffscreen && !visibilityRef.current.visible) {\n          raf = requestAnimationFrame(animate);\n          return;\n        }\n        uniforms.uTime.value = timeOffset + clock.getElapsedTime() * speedRef.current;\n        if (liquidEffect) {\n          const liqEffect = liquidEffect as Effect & { uniforms: Map<string, THREE.Uniform> };\n          const timeUniform = liqEffect.uniforms.get('uTime');\n          if (timeUniform) timeUniform.value = uniforms.uTime.value;\n        }\n        if (composer) {\n          if (touch) touch.update();\n          composer.passes.forEach(p => {\n            const pass = p as { effects?: Array<Effect & { uniforms: Map<string, THREE.Uniform> }> };\n            if (pass.effects) {\n              pass.effects.forEach(eff => {\n                const timeUniform = eff.uniforms?.get('uTime');\n                if (timeUniform) timeUniform.value = uniforms.uTime.value;\n              });\n            }\n          });\n          composer.render();\n        } else renderer.render(scene, camera);\n        raf = requestAnimationFrame(animate);\n      };\n      raf = requestAnimationFrame(animate);\n      threeRef.current = {\n        renderer,\n        scene,\n        camera,\n        material,\n        clock,\n        clickIx: 0,\n        uniforms,\n        resizeObserver: ro,\n        raf,\n        quad,\n        timeOffset,\n        composer,\n        touch,\n        liquidEffect\n      };\n    } else {\n      const t = threeRef.current!;\n      t.uniforms.uShapeType.value = SHAPE_MAP[variant] ?? 0;\n      t.uniforms.uPixelSize.value = pixelSize * t.renderer.getPixelRatio();\n      t.uniforms.uColor.value.set(color);\n      t.uniforms.uScale.value = patternScale;\n      t.uniforms.uDensity.value = patternDensity;\n      t.uniforms.uPixelJitter.value = pixelSizeJitter;\n      t.uniforms.uEnableRipples.value = enableRipples ? 1 : 0;\n      t.uniforms.uRippleIntensity.value = rippleIntensityScale;\n      t.uniforms.uRippleThickness.value = rippleThickness;\n      t.uniforms.uRippleSpeed.value = rippleSpeed;\n      t.uniforms.uEdgeFade.value = edgeFade;\n      if (transparent) t.renderer.setClearAlpha(0);\n      else t.renderer.setClearColor(0x000000, 1);\n      if (t.liquidEffect) {\n        const liqEffect = t.liquidEffect as Effect & { uniforms: Map<string, THREE.Uniform> };\n        const uStrength = liqEffect.uniforms.get('uStrength');\n        if (uStrength) uStrength.value = liquidStrength;\n        const uFreq = liqEffect.uniforms.get('uFreq');\n        if (uFreq) uFreq.value = liquidWobbleSpeed;\n      }\n      if (t.touch) t.touch.radiusScale = liquidRadius;\n    }\n    prevConfigRef.current = cfg;\n    return () => {\n      if (threeRef.current && mustReinit) return;\n      if (!threeRef.current) return;\n      const t = threeRef.current;\n      t.resizeObserver?.disconnect();\n      cancelAnimationFrame(t.raf!);\n      t.quad?.geometry.dispose();\n      t.material.dispose();\n      t.composer?.dispose();\n      t.renderer.dispose();\n      t.renderer.forceContextLoss();\n      if (t.renderer.domElement.parentElement === container) container.removeChild(t.renderer.domElement);\n      threeRef.current = null;\n    };\n  }, [\n    antialias,\n    liquid,\n    noiseAmount,\n    pixelSize,\n    patternScale,\n    patternDensity,\n    enableRipples,\n    rippleIntensityScale,\n    rippleThickness,\n    rippleSpeed,\n    pixelSizeJitter,\n    edgeFade,\n    transparent,\n    liquidStrength,\n    liquidRadius,\n    liquidWobbleSpeed,\n    autoPauseOffscreen,\n    variant,\n    color,\n    speed\n  ]);\n\n  return (\n    <div\n      ref={containerRef}\n      className={`pixel-blast-container ${className ?? ''}`}\n      style={style}\n      aria-label=\"PixelBlast interactive background\"\n    />\n  );\n};\n\nexport default PixelBlast;\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"postprocessing@^6.36.0",
		"three@^0.180.0"
	]
}