feat: secondary color gradient on VU meter bars

Extract a hue-distinct secondary color from album art and apply a
primary→secondary gradient across the VU meter bars (bass end = primary,
treble end = secondary). Falls back to complementary hue if no distinct
color is found.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ltadeu6 2026-06-07 13:10:24 -03:00
parent 1957fda57e
commit 7ed455f72b
No known key found for this signature in database
GPG key ID: FB9FDAB6B6F3418D

View file

@ -1014,11 +1014,11 @@ requestAnimationFrame(flushBars);
/* ============================================================ /* ============================================================
MUSIC-LIGHT THEME (album color) MUSIC-LIGHT THEME (album color)
============================================================ */ ============================================================ */
function applyTheme(rgb) { function applyTheme(rgb, rgb2) {
const [r, g, b] = rgb; const [r, g, b] = rgb;
const color = `rgba(${r},${g},${b},1)`; const [r2, g2, b2] = rgb2 || [r, g, b];
document.documentElement.style.setProperty('--theme-color', color); document.documentElement.style.setProperty('--theme-color', `rgba(${r},${g},${b},1)`);
// Tint horizon // Tint horizon with primary color
horizonEl.style.background = `linear-gradient(to right, horizonEl.style.background = `linear-gradient(to right,
transparent 0%, transparent 0%,
rgba(${r},${g},${b},0.7) 20%, rgba(${r},${g},${b},0.7) 20%,
@ -1027,6 +1027,15 @@ function applyTheme(rgb) {
transparent 100%)`; transparent 100%)`;
horizonEl.style.boxShadow = horizonEl.style.boxShadow =
`0 0 30px rgba(${r},${g},${b},0.9), 0 0 90px rgba(${r},${g},${b},0.3)`; `0 0 30px rgba(${r},${g},${b},0.9), 0 0 90px rgba(${r},${g},${b},0.3)`;
// Apply primary→secondary gradient across VU bars
vuBars.forEach((bar, i) => {
const t = i / (vuBars.length - 1);
const br = Math.round(r + (r2 - r) * t);
const bg = Math.round(g + (g2 - g) * t);
const bb = Math.round(b + (b2 - b) * t);
bar.style.background = `rgb(${br},${bg},${bb})`;
bar.style.boxShadow = `0 0 4px rgba(${br},${bg},${bb},0.8)`;
});
} }
const albumArtEl = document.getElementById('albumArt'); const albumArtEl = document.getElementById('albumArt');
@ -1037,7 +1046,7 @@ function fetchTheme() {
fetch('http://127.0.0.1:8765/current') fetch('http://127.0.0.1:8765/current')
.then(r => r.json()) .then(r => r.json())
.then(d => { .then(d => {
if (d.rgb) applyTheme(d.rgb); if (d.rgb) applyTheme(d.rgb, d.rgb2);
if (d.art_url && d.art_url !== lastArtUrl) { if (d.art_url && d.art_url !== lastArtUrl) {
lastArtUrl = d.art_url; lastArtUrl = d.art_url;
document.getElementById('albumArtImg').src = document.getElementById('albumArtImg').src =