import React, { useEffect, useRef } from 'react'; interface VisualizerProps { analyser: AnalyserNode | null; active: boolean; } const Visualizer: React.FC = ({ analyser, active }) => { const canvasRef = useRef(null); useEffect(() => { if (!canvasRef.current) return; const canvas = canvasRef.current; const ctx = canvas.getContext('2d'); if (!ctx) return; let animationId: number; const bufferLength = analyser ? analyser.frequencyBinCount : 0; const dataArray = new Uint8Array(bufferLength); const draw = () => { animationId = requestAnimationFrame(draw); const width = canvas.width; const height = canvas.height; ctx.clearRect(0, 0, width, height); if (!active || !analyser) { // Idle state: gentle pulse const time = Date.now() / 1000; ctx.beginPath(); ctx.arc(width / 2, height / 2, 20 + Math.sin(time * 2) * 2, 0, Math.PI * 2); ctx.fillStyle = 'rgba(99, 102, 241, 0.5)'; ctx.fill(); return; } analyser.getByteFrequencyData(dataArray); const barWidth = (width / bufferLength) * 2.5; let barHeight; let x = 0; // Draw mirrored spectrum from center const centerX = width / 2; for (let i = 0; i < bufferLength; i++) { barHeight = dataArray[i] / 2; // Gradient color const gradient = ctx.createLinearGradient(0, height / 2 - barHeight, 0, height / 2 + barHeight); gradient.addColorStop(0, '#818cf8'); // indigo-400 gradient.addColorStop(1, '#c084fc'); // purple-400 ctx.fillStyle = gradient; // Right side ctx.fillRect(centerX + x, height / 2 - barHeight / 2, barWidth, barHeight); // Left side ctx.fillRect(centerX - x - barWidth, height / 2 - barHeight / 2, barWidth, barHeight); x += barWidth + 1; } }; draw(); return () => cancelAnimationFrame(animationId); }, [analyser, active]); return ( ); }; export default Visualizer;