"use client"; import { useEffect, useRef } from "react"; interface WaveformVisualizerProps { analyserNode: AnalyserNode | null; isActive: boolean; height?: number; color?: string; } export default function WaveformVisualizer({ analyserNode, isActive, height = 80, color = "#8B5CF6", }: WaveformVisualizerProps) { const canvasRef = useRef(null); const animFrameRef = useRef(0); useEffect(() => { const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext("2d"); if (!ctx) return; const draw = () => { animFrameRef.current = requestAnimationFrame(draw); const W = canvas.width; const H = canvas.height; ctx.clearRect(0, 0, W, H); if (!analyserNode || !isActive) { // Idle animation — gentle sine wave ctx.beginPath(); const t = Date.now() / 800; for (let x = 0; x < W; x++) { const y = H / 2 + Math.sin(x * 0.03 + t) * 8 * Math.sin(x * 0.008 + t * 0.5); x === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y); } ctx.strokeStyle = color + "60"; ctx.lineWidth = 2; ctx.stroke(); return; } const bufLen = analyserNode.frequencyBinCount; const dataArray = new Uint8Array(bufLen); analyserNode.getByteTimeDomainData(dataArray); // Draw gradient waveform const gradient = ctx.createLinearGradient(0, 0, W, 0); gradient.addColorStop(0, "#8B5CF6"); gradient.addColorStop(0.5, "#14b8a6"); gradient.addColorStop(1, "#8B5CF6"); ctx.beginPath(); const sliceW = W / bufLen; let x = 0; for (let i = 0; i < bufLen; i++) { const v = dataArray[i] / 128.0; const y = (v * H) / 2; i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y); x += sliceW; } ctx.lineTo(W, H / 2); ctx.strokeStyle = gradient; ctx.lineWidth = 2.5; ctx.shadowBlur = 12; ctx.shadowColor = color; ctx.stroke(); ctx.shadowBlur = 0; }; draw(); return () => cancelAnimationFrame(animFrameRef.current); }, [analyserNode, isActive, color]); return ( ); }