diff --git a/web/static/app.js b/web/static/app.js index a45b244..87fbcaa 100644 --- a/web/static/app.js +++ b/web/static/app.js @@ -269,6 +269,21 @@ function renderFrame(ts = 0) { // Check if real viz data is fresh (< 1.5 s old) and Winamp is playing. const hasSignal = winampPlaying && (performance.now() - lastVizAt) < 1500; + // Classic fixed vertical gradient: green at bottom → yellow → red at top. + // Defined in canvas coords so every bar shows the same colour at the same + // height — no per-frame hue calculation, no flicker. + const grad = ctx2d.createLinearGradient(0, h, 0, 0); + if (hasSignal) { + grad.addColorStop(0, '#00aa00'); + grad.addColorStop(0.6, '#aaaa00'); + grad.addColorStop(1, '#cc0000'); + } else { + // Idle: same palette but much dimmer + grad.addColorStop(0, '#003300'); + grad.addColorStop(0.6, '#333300'); + grad.addColorStop(1, '#330000'); + } + const n = NUM_BARS; const gap = 1; const barW = Math.max(1, (w - gap * (n - 1)) / n); @@ -277,34 +292,29 @@ function renderFrame(ts = 0) { let val; if (hasSignal) { - // Real spectrum data val = lastBars[i] || 0; if (val > peaks[i]) peaks[i] = val; - else peaks[i] = Math.max(0, peaks[i] - 0.012); + else peaks[i] = Math.max(0, peaks[i] - 0.008); // gentle decay } else { - // Idle animation: slow sine "breathing" across the bars. - // Amplitude fades out when paused/stopped. + // Idle: slow sine breathing const phase = (ts / 1800) + (i / n) * Math.PI * 2; - const breath = (Math.sin(ts / 2000) * 0.5 + 0.5) * 0.08; // 0..0.08 + const breath = (Math.sin(ts / 2000) * 0.5 + 0.5) * 0.08; val = Math.max(0, Math.sin(phase) * breath); - peaks[i] = Math.max(0, peaks[i] - 0.02); // let peaks fall quickly + peaks[i] = Math.max(0, peaks[i] - 0.02); } const x = Math.round(i * (barW + gap)); const barH = val * h; if (barH > 0.5) { - // Bar colour: green (120°) → yellow (60°) → red (0°) - const hue = Math.round((1 - val) * 120); - const lit = hasSignal ? 42 : 25; // dimmer in idle - ctx2d.fillStyle = `hsl(${hue},100%,${lit}%)`; + ctx2d.fillStyle = grad; ctx2d.fillRect(x, h - barH, barW, barH); } - // Peak indicator + // Peak dot — 1 px taller slice of the same gradient, slightly brighter if (peaks[i] > 0.02) { const py = Math.round(h - peaks[i] * h) - 1; - ctx2d.fillStyle = hasSignal ? 'rgba(255,255,255,0.75)' : 'rgba(255,255,255,0.2)'; + ctx2d.fillStyle = hasSignal ? 'rgba(255,255,255,0.6)' : 'rgba(255,255,255,0.15)'; ctx2d.fillRect(x, py, barW, 2); } }