Kaynağa Gözat

fix: classic spectrum — fixed gradient, no hue flicker

Replace per-frame hsl(val*120) with a static createLinearGradient
(green->yellow->red, bottom to top) shared across all bars. Colour is
now position-based, not amplitude-based, so it never flickers. Peak
decay slightly slower (0.008/frame) for smoother hold.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
master
Jan Svabenik 1 ay önce
ebeveyn
işleme
af624bbd77
1 değiştirilmiş dosya ile 22 ekleme ve 12 silme
  1. +22
    -12
      web/static/app.js

+ 22
- 12
web/static/app.js Dosyayı Görüntüle

@@ -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);
}
}


Yükleniyor…
İptal
Kaydet