{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "glass-waveform",
  "title": "Glass Waveform",
  "description": "An audio visualizer with liquid glass bars, gradient fills, and a frozen glass paused state.",
  "files": [
    {
      "path": "registry/innovative/glass-waveform.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"@/lib/utils\"\n\ninterface GlassWaveformProps extends React.HTMLAttributes<HTMLDivElement> {\n  /** Base amplitude of the waveform (clamped to 0–1). */\n  amplitude?: number\n  /** Number of frequency bars (clamped to 4–96). */\n  bars?: number\n  /** Color scheme for the bars. */\n  color?: \"cyan\" | \"purple\" | \"emerald\" | \"gradient\"\n  /** Freezes the visualizer into a \"frozen glass\" state. */\n  paused?: boolean\n  /** Shows a readout of the current visualizer settings. */\n  showLabels?: boolean\n}\n\nconst DEFAULT_BARS = 32\nconst MIN_BARS = 4\nconst MAX_BARS = 96\nconst MAX_BAR_HEIGHT = 60\n\nconst colorVariants = {\n  cyan: \"from-cyan-400 to-blue-500\",\n  purple: \"from-fuchsia-400 to-violet-500\",\n  emerald: \"from-emerald-400 to-teal-500\",\n  gradient: \"from-cyan-400 via-blue-400 to-purple-400\",\n}\n\n/** Deterministic seed so server and client render identical initial bars. */\nfunction seedHeight(index: number) {\n  return 0.25 + 0.5 * Math.abs(Math.sin(index * 1.7 + 0.4))\n}\n\nconst GlassWaveform = React.forwardRef<HTMLDivElement, GlassWaveformProps>(\n  (\n    {\n      className,\n      amplitude = 0.8,\n      bars = DEFAULT_BARS,\n      color = \"gradient\",\n      paused = false,\n      showLabels = false,\n      ...props\n    },\n    ref,\n  ) => {\n    const gradientId = React.useId()\n    const barRefs = React.useRef<Array<HTMLDivElement | null>>([])\n    const frameRef = React.useRef<number | null>(null)\n    const [reducedMotion, setReducedMotion] = React.useState(false)\n\n    const barCount = Math.max(MIN_BARS, Math.min(MAX_BARS, Math.round(bars)))\n    const amplitudeSafe = Math.max(0, Math.min(1, amplitude))\n\n    // Respect the user's reduced-motion preference.\n    React.useEffect(() => {\n      if (typeof window.matchMedia !== \"function\") return\n\n      const mql = window.matchMedia(\"(prefers-reduced-motion: reduce)\")\n      const onChange = () => setReducedMotion(mql.matches)\n      onChange()\n      mql.addEventListener(\"change\", onChange)\n      return () => mql.removeEventListener(\"change\", onChange)\n    }, [])\n\n    // Bar refs are populated by the ref callbacks below on every commit; do NOT\n    // clear them here (a post-mount clear would wipe the DOM nodes the animation\n    // loop reads and leave the visualizer frozen on the initial mount).\n\n    // Animate the bars by writing heights directly to the DOM (no re-renders).\n    React.useEffect(() => {\n      if (paused || reducedMotion) return\n\n      // Resume smoothly from the current (possibly frozen) heights.\n      const heights = barRefs.current.map((node, index) => {\n        const current = node ? parseFloat(node.style.height) : Number.NaN\n        return Number.isFinite(current) ? (current - 16) / MAX_BAR_HEIGHT : seedHeight(index)\n      })\n\n      const update = (time: number) => {\n        for (let index = 0; index < barCount; index++) {\n          const node = barRefs.current[index]\n          if (!node) continue\n\n          const drift = Math.sin(time / 450 + index * 0.35) * 0.14\n          const target = 0.15 + Math.abs(Math.sin(time / 300 + index * 0.25)) * amplitudeSafe\n          heights[index] = Math.max(\n            0.05,\n            Math.min(1, heights[index] + (target - heights[index]) * 0.16 + drift * 0.04),\n          )\n\n          node.style.height = `${16 + heights[index] * MAX_BAR_HEIGHT}px`\n        }\n\n        frameRef.current = window.requestAnimationFrame(update)\n      }\n\n      frameRef.current = window.requestAnimationFrame(update)\n      return () => {\n        if (frameRef.current) window.cancelAnimationFrame(frameRef.current)\n      }\n    }, [amplitudeSafe, barCount, paused, reducedMotion])\n\n    const gradientClass = colorVariants[color]\n\n    return (\n      <div\n        ref={ref}\n        className={cn(\n          \"relative overflow-hidden rounded-4xl border border-white/10 bg-black/25 backdrop-blur-xl\",\n          className,\n        )}\n        {...props}\n      >\n        <div className=\"absolute inset-0 bg-[radial-gradient(circle_at_top_left,rgba(255,255,255,0.18),transparent_25%),radial-gradient(circle_at_bottom_right,rgba(59,130,246,0.12),transparent_30%)]\" />\n\n        {/* Decorative drawing — hidden from assistive technology. */}\n        <div className=\"relative h-52 px-4 py-5\" aria-hidden=\"true\">\n          <div className=\"absolute inset-x-4 top-4 h-0.5 bg-white/10\" />\n          <div className=\"absolute inset-x-4 bottom-4 h-0.5 bg-white/10\" />\n\n          <div className=\"absolute inset-0 pointer-events-none\">\n            <svg viewBox=\"0 0 100 100\" className=\"w-full h-full opacity-30\">\n              <defs>\n                <linearGradient id={gradientId} x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"0%\">\n                  <stop offset=\"0%\" stopColor=\"#22d3ee\" />\n                  <stop offset=\"50%\" stopColor=\"#818cf8\" />\n                  <stop offset=\"100%\" stopColor=\"#e879f9\" />\n                </linearGradient>\n              </defs>\n              <path\n                d=\"M0,60 C20,40 40,65 50,45 C60,25 80,50 100,35\"\n                fill=\"none\"\n                stroke={`url(#${gradientId})`}\n                strokeWidth=\"1.4\"\n                opacity=\"0.75\"\n              />\n            </svg>\n          </div>\n\n          <div className=\"relative h-full flex items-center justify-between gap-2\">\n            {Array.from({ length: barCount }, (_, index) => (\n              <div key={index} className=\"flex-1 h-full flex flex-col justify-end\">\n                <div\n                  ref={(node) => {\n                    barRefs.current[index] = node\n                  }}\n                  className={cn(\n                    \"mx-auto w-full rounded-full\",\n                    paused ? \"bg-white/10\" : `bg-linear-to-t ${gradientClass}`,\n                  )}\n                  style={{\n                    height: 16 + seedHeight(index) * MAX_BAR_HEIGHT,\n                    minHeight: 4,\n                    transition: \"background 300ms ease\",\n                  }}\n                >\n                  <div className=\"h-full rounded-full bg-white/10 mix-blend-screen\" />\n                </div>\n              </div>\n            ))}\n          </div>\n\n          <div className=\"pointer-events-none absolute inset-x-4 top-6 h-0.75 bg-white/10 blur-sm\" />\n        </div>\n\n        <div className=\"border-t border-white/10 px-5 py-3 bg-black/40 backdrop-blur-xl\">\n          <div className=\"flex items-center justify-between gap-4 text-xs uppercase tracking-[0.3em] text-white/60\">\n            <span className=\"font-semibold\">Audio Visualizer</span>\n            <span className={cn(paused ? \"text-rose-200\" : \"text-emerald-200\")}>\n              {paused ? \"Frozen Glass\" : \"Live\"}\n            </span>\n          </div>\n          {showLabels ? (\n            <div className=\"mt-3 grid grid-cols-3 gap-3 text-[11px] text-white/50\">\n              <span>Amplitude {Math.round(amplitudeSafe * 100)}%</span>\n              <span>Bars {barCount}</span>\n              <span>Mode {paused ? \"Paused\" : \"Active\"}</span>\n            </div>\n          ) : null}\n        </div>\n      </div>\n    )\n  },\n)\n\nGlassWaveform.displayName = \"GlassWaveform\"\n\nexport { GlassWaveform }\nexport type { GlassWaveformProps }\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}