document.addEventListener('DOMContentLoaded', () => { const startButton = document.getElementById('startButton'); const canvas = document.getElementById('visualizer'); const statusEl = document.getElementById('status'); const canvasCtx = canvas.getContext('2d'); let audioContext; let analyser; let dataArray; let bufferLength; let animationFrameId; let isVisualizing = false; const draw = () => { animationFrameId = requestAnimationFrame(draw); analyser.getByteTimeDomainData(dataArray); canvasCtx.fillStyle = '#f8f9fa'; // Light gray background canvasCtx.fillRect(0, 0, canvas.width, canvas.height); const gradient = canvasCtx.createLinearGradient(0, 0, canvas.width, 0); gradient.addColorStop(0, '#6A5ACD'); // SlateBlue gradient.addColorStop(1, '#48D1CC'); // MediumTurquoise canvasCtx.strokeStyle = gradient; canvasCtx.lineWidth = 3; canvasCtx.beginPath(); const sliceWidth = canvas.width * 1.0 / bufferLength; let x = 0; for (let i = 0; i < bufferLength; i++) { const v = dataArray[i] / 128.0; const y = v * canvas.height / 2; if (i === 0) { canvasCtx.moveTo(x, y); } else { canvasCtx.lineTo(x, y); } x += sliceWidth; } canvasCtx.lineTo(canvas.width, canvas.height / 2); canvasCtx.stroke(); }; const startVisualization = async () => { if (isVisualizing) return; try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false }); audioContext = new (window.AudioContext || window.webkitAudioContext)(); analyser = audioContext.createAnalyser(); const source = audioContext.createMediaStreamSource(stream); source.connect(analyser); analyser.fftSize = 2048; bufferLength = analyser.frequencyBinCount; dataArray = new Uint8Array(bufferLength); canvasCtx.clearRect(0, 0, canvas.width, canvas.height); isVisualizing = true; startButton.disabled = true; startButton.innerHTML = ` Visualizing...`; statusEl.textContent = 'Your voice is being visualized in real-time.'; draw(); } catch (err) { console.error('Error accessing microphone:', err); statusEl.textContent = 'Could not access microphone. Please check permissions and try again.'; statusEl.style.color = 'red'; } }; startButton.addEventListener('click', startVisualization); // Clean up on page unload window.addEventListener('beforeunload', () => { if (audioContext) { audioContext.close(); } if (animationFrameId) { cancelAnimationFrame(animationFrameId); } }); });