fix(visualizer): improve responsiveness and visibility

- Increase FFT emit rate: ~21fps → ~60fps (interval 2048 → 735 samples)
- Boost scale factor: 4x → 50x for more visible response
- Gentler log mapping: powf(2.0) → powf(1.4) for better spread across bars
- Faster smoothing: attack 0.7→0.85, decay 0.15→0.5
- Faster CSS transitions: 80ms → 35ms
- Solid colors instead of gradients for better visibility
This commit is contained in:
vicrodh committed 2026-01-30 22:20:11 -06:00
1 parent 16b35c33fd
commit 7e17b74f99
2 files changed
+18 -20

No files matched your search

+12 -13
View File
@@ -12,8 +12,8 @@ const OUTPUT_BINS: usize = 32;
/// FFT window size (power of 2 for efficiency)
const FFT_SIZE: usize = 1024;
/// How often to compute FFT (in samples) - roughly 20-30fps at 44.1kHz
const FFT_INTERVAL: usize = 2048;
/// How often to compute FFT (in samples) - ~60fps at 44.1kHz
const FFT_INTERVAL: usize = 735;
/// Visualizer state shared between audio thread and emitter
pub struct VisualizerState {
@@ -133,34 +133,33 @@ impl VisualizerState {
let avg = if count > 0 { sum / count as f32 } else { 0.0 };
// Scale to 0-1 range (with some headroom)
// The scaling factor is empirical - adjust for visual effect
let scaled = (avg * 4.0 / FFT_SIZE as f32).min(1.0);
// Scale to 0-1 range - higher multiplier for more visible response
let scaled = (avg * 50.0 / FFT_SIZE as f32).min(1.0);
self.frequency_bins[i] = scaled;
}
// Apply smoothing (fast attack, slow decay)
// Apply smoothing (fast attack, fast decay for responsive feel)
for i in 0..OUTPUT_BINS {
let current = self.frequency_bins[i];
let smoothed = self.smoothed_bins[i];
if current > smoothed {
// Fast attack
self.smoothed_bins[i] = smoothed + (current - smoothed) * 0.7;
// Very fast attack
self.smoothed_bins[i] = smoothed + (current - smoothed) * 0.85;
} else {
// Slow decay
self.smoothed_bins[i] = smoothed + (current - smoothed) * 0.15;
// Fast decay for snappy response
self.smoothed_bins[i] = smoothed + (current - smoothed) * 0.5;
}
}
}
/// Logarithmic bin mapping for perceptual frequency distribution
fn log_bin_start(&self, bin: usize, total_bins: usize) -> usize {
// Map 0..OUTPUT_BINS to 0..total_bins logarithmically
// This gives more resolution to lower frequencies
// Map 0..OUTPUT_BINS to 0..total_bins with mild log curve
// Less aggressive = more even distribution across all bars
let ratio = bin as f32 / OUTPUT_BINS as f32;
let log_ratio = ratio.powf(2.0); // Quadratic gives good distribution
let log_ratio = ratio.powf(1.4); // Gentler curve for better spread
(log_ratio * total_bins as f32) as usize
}
@@ -187,13 +187,12 @@
flex: 1;
max-width: 20px;
height: var(--height);
background: linear-gradient(to top, var(--color), transparent);
background: var(--color);
border-radius: 4px 4px 0 0;
/* GPU accelerated - only transform, no layout */
transform: scaleY(1);
transform-origin: bottom;
transition: height 80ms ease-out;
transition: height 35ms linear;
will-change: height;
opacity: 0.9;
}
/* Wave mode */
@@ -209,7 +208,7 @@
}
.wave-svg path {
transition: d 80ms ease-out;
transition: d 35ms linear;
}
/* Mirror mode - bars going up and down from center */
@@ -238,9 +237,9 @@
width: 100%;
height: var(--height);
max-height: 48%;
background: linear-gradient(to top, var(--color), transparent);
background: var(--color);
border-radius: 3px;
transition: height 80ms ease-out;
transition: height 35ms linear;
will-change: height;
}