feat(ui): live VU + audio-driven spectrum, editorial banner, subtler dynamic bg
VU needle (animated) - Synthetic wobble bounded by current volume runs only while state==='playing'. Two combined sines + jitter make it look like a real analog needle reacting to peaks. - Settles back to the static volume-mapped position when paused. Spectrum (real audio) - Now driven by the same frequencyData the visualizer canvas uses. Each visual bar averages a chunk of frequency bins. - Spans are now JS-injected (60 bars) instead of hardcoded so the bar count is no longer baked in. - Spectrum spans full width of the masthead column, height bumped to 56px for presence. - CSS animation pauses (sets via `body.audio-spectrum-live`) when JS is driving heights so the keyframes don't fight. - Synthetic CSS animation remains as the fallback when audio capture isn't available. Visualizer auto-enable - On first install with loopback support, visualizer is enabled so the spectrum is alive out of the box. Dynamic background - Lower max opacity (1 → 0.45 dark, 0.35 light) - sepia + saturate filter + hue-rotate keep it palette-aligned with the copper editorial tones instead of fighting them - mix-blend-mode screen (dark) / multiply (light) blends into the page background instead of overlaying Update + connection banners - Fully restyled: glassy card with copper hairline accent, mono uppercase text, copper hairline-border CTA buttons, minimal close button. Matches the rest of the editorial palette instead of the old solid-green-bar look.
This commit is contained in:
@@ -166,9 +166,33 @@ window.addEventListener('DOMContentLoaded', async () => {
|
||||
// Vinyl is now structural / always-on via CSS — no init call needed.
|
||||
// applyVinylMode();
|
||||
|
||||
// Initialize audio visualizer
|
||||
// Build the editorial spectrum bars (60 spans). JS-managed so we can
|
||||
// drive heights from real audio data when available.
|
||||
const spectrumRoot = document.getElementById('player-spectrum');
|
||||
if (spectrumRoot && !spectrumRoot.children.length) {
|
||||
const SPECTRUM_BARS = 60;
|
||||
const frag = document.createDocumentFragment();
|
||||
for (let i = 0; i < SPECTRUM_BARS; i++) {
|
||||
const s = document.createElement('span');
|
||||
// Pseudo-random heights for the synthetic CSS animation phase
|
||||
s.style.setProperty('--bar-h', (25 + Math.abs(Math.sin(i * 0.7)) * 70).toFixed(0) + '%');
|
||||
s.style.setProperty('--bar-delay', (-Math.random() * 1.1).toFixed(2) + 's');
|
||||
frag.appendChild(s);
|
||||
}
|
||||
spectrumRoot.appendChild(frag);
|
||||
}
|
||||
|
||||
// Initialize audio visualizer — auto-enable when supported so the
|
||||
// spectrum shows real audio out of the box.
|
||||
checkVisualizerAvailability().then(() => {
|
||||
if (visualizerEnabled && visualizerAvailable) {
|
||||
if (visualizerAvailable && !visualizerEnabled) {
|
||||
// Auto-enable on first install if loopback capture works.
|
||||
if (localStorage.getItem('visualizerEnabled') === null) {
|
||||
localStorage.setItem('visualizerEnabled', 'true');
|
||||
}
|
||||
}
|
||||
if ((visualizerEnabled || localStorage.getItem('visualizerEnabled') === 'true')
|
||||
&& visualizerAvailable) {
|
||||
applyVisualizerMode();
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user