diff --git a/index.php b/index.php index 6d38fcb..e39c5d6 100644 --- a/index.php +++ b/index.php @@ -110,12 +110,13 @@ $liveIndex = get_live_index($schedule); background-position: center; background-repeat: no-repeat; animation: ken-burns 60s ease-in-out infinite alternate; - transition: background-image 2s ease-in-out; + transition: background-image 2s ease-in-out, transform 0.1s ease-out; + transform: scale(var(--pulse-intensity, 1)); } @keyframes ken-burns { - 0% { transform: scale(1); } - 100% { transform: scale(1.1); } + 0% { transform: scale(var(--pulse-intensity, 1)); } + 100% { transform: scale(calc(var(--pulse-intensity, 1) * 1.1)); } } .background-overlay { @@ -128,6 +129,8 @@ $liveIndex = get_live_index($schedule); background: radial-gradient(circle at top right, rgba(56, 189, 248, 0.1), transparent 40%), radial-gradient(circle at bottom left, rgba(244, 114, 182, 0.1), transparent 40%), linear-gradient(135deg, rgba(15, 23, 42, 0.4) 0%, rgba(15, 23, 42, 0.7) 100%); + transition: opacity 0.1s ease-out; + opacity: calc(0.7 + (var(--pulse-intensity, 1) - 1) * 2); } /* Floating Microphones Effect */ @@ -682,6 +685,7 @@ $liveIndex = get_live_index($schedule); mic.style.setProperty("--move-y", (Math.random() - 0.5) * 600 + "px"); mic.style.animationDelay = Math.random() * -25 + "s"; micBg.appendChild(mic); + floatingMics.push(mic); } const audio = document.getElementById("radio-audio"); @@ -696,7 +700,10 @@ $liveIndex = get_live_index($schedule); let analyser; let source; let dataArray; + let freqDataArray; let animationId; + let floatingMics = []; + let particles = []; let waveColor = "rgba(56, 189, 248, 0.8)"; // Default primary let waveGlow = "rgba(56, 189, 248, 0.4)"; @@ -717,20 +724,84 @@ $liveIndex = get_live_index($schedule); analyser.fftSize = 2048; const bufferLength = analyser.frequencyBinCount; dataArray = new Uint8Array(bufferLength); + freqDataArray = new Uint8Array(bufferLength); } } function drawWave() { animationId = requestAnimationFrame(drawWave); analyser.getByteTimeDomainData(dataArray); + analyser.getByteFrequencyData(freqDataArray); + + // Calculate average volume for pulse effect + let sum = 0; + for (let i = 0; i < 30; i++) { // Focus on bass frequencies + sum += freqDataArray[i]; + } + const average = sum / 30; + const boost = (average / 128.0); // Factor between 0 and 2 + + // Update CSS variables for background pulse + document.body.style.setProperty("--pulse-intensity", (1 + boost * 0.15).toString()); + floatingMics.forEach(mic => { + mic.style.transform = `translate(var(--move-x), var(--move-y)) rotate(360deg) scale(${1 + boost * 0.5})`; + }); canvasCtx.clearRect(0, 0, canvas.width, canvas.height); - // Subtle glow effect - canvasCtx.shadowBlur = 15; + // --- Particle System (Fire/Sparks) --- + if (boost > 1.2) { // Threshold for intense bass + const particleCount = Math.floor(boost * 3); + for (let i = 0; i < particleCount; i++) { + const x = Math.random() * canvas.width; + // Find approximate y of the wave at this x + const dataIdx = Math.floor((x / canvas.width) * dataArray.length); + const v = dataArray[dataIdx] / 128.0; + const amplitude = (v - 1) * (1.5 + boost * 2) + 1; + const y = amplitude * canvas.height / 2; + + particles.push({ + x: x, + y: y, + vx: (Math.random() - 0.5) * 4, + vy: -Math.random() * 6 - (boost * 4), + alpha: 1, + size: Math.random() * 3 + 1, + color: waveColor.replace("0.8", "1") // More solid for sparks + }); + } + } + + // Update and draw particles + for (let i = particles.length - 1; i >= 0; i--) { + const p = particles[i]; + p.x += p.vx; + p.y += p.vy; + p.vy += 0.2; // Gravity + p.alpha -= 0.02; // Fade out + + if (p.alpha <= 0) { + particles.splice(i, 1); + continue; + } + + canvasCtx.save(); + canvasCtx.globalAlpha = p.alpha; + canvasCtx.fillStyle = p.color; + canvasCtx.shadowBlur = 5; + canvasCtx.shadowColor = p.color; + canvasCtx.beginPath(); + canvasCtx.arc(p.x, p.y, p.size, 0, Math.PI * 2); + canvasCtx.fill(); + canvasCtx.restore(); + } + // --- End Particle System --- + + // Dynamic glow based on volume + canvasCtx.shadowBlur = 10 + (boost * 20); canvasCtx.shadowColor = waveGlow; - canvasCtx.lineWidth = 3; + canvasCtx.lineWidth = 2 + (boost * 3); canvasCtx.strokeStyle = waveColor; canvasCtx.beginPath(); @@ -739,7 +810,9 @@ $liveIndex = get_live_index($schedule); for (let i = 0; i < dataArray.length; i++) { const v = dataArray[i] / 128.0; - const y = v * canvas.height / 2; + // Scale amplitude by boost factor for more intensity + const amplitude = (v - 1) * (1.5 + boost * 2) + 1; + const y = amplitude * canvas.height / 2; if (i === 0) canvasCtx.moveTo(x, y); else canvasCtx.lineTo(x, y); @@ -750,7 +823,6 @@ $liveIndex = get_live_index($schedule); canvasCtx.lineTo(canvas.width, canvas.height / 2); canvasCtx.stroke(); - // Reset shadow for performance canvasCtx.shadowBlur = 0; }