From f9bdabc0e4a42638535ecbfbef426af2f51ba27f Mon Sep 17 00:00:00 2001
From: Broque Thomas <26755000+Nezreka@users.noreply.github.com>
Date: Fri, 13 Mar 2026 16:05:22 -0700
Subject: [PATCH] Add particle background toggle & optimize accent color
caching
---
webui/index.html | 7 +++++++
webui/static/particles.js | 13 +++++++++---
webui/static/script.js | 43 +++++++++++++++++++++++++++++++++++++--
3 files changed, 58 insertions(+), 5 deletions(-)
diff --git a/webui/index.html b/webui/index.html
index fb2c5573..662df1d7 100644
--- a/webui/index.html
+++ b/webui/index.html
@@ -4195,6 +4195,13 @@
Audio-reactive visualizer on the sidebar edge when music is playing
+
+
+ Animated particle effects behind each page. Disable to reduce GPU usage.
+
diff --git a/webui/static/particles.js b/webui/static/particles.js
index e54dc2b8..a390db9c 100644
--- a/webui/static/particles.js
+++ b/webui/static/particles.js
@@ -21,9 +21,15 @@
// ── Helpers ──
+ let _cachedAccent = '29, 185, 84';
+ let _accentCheckFrame = 0;
function getAccentRGB() {
- const s = getComputedStyle(document.documentElement).getPropertyValue('--accent-rgb').trim();
- return s || '29, 185, 84';
+ // Only re-read CSS variable every 60 frames (~1s) to avoid getComputedStyle overhead
+ if (_accentCheckFrame++ % 60 === 0) {
+ const s = getComputedStyle(document.documentElement).getPropertyValue('--accent-rgb').trim();
+ if (s) _cachedAccent = s;
+ }
+ return _cachedAccent;
}
// Shift an "r, g, b" accent string by a hue offset (degrees), cached per base color
@@ -2286,8 +2292,9 @@
stop
};
- // Auto-start for initial page
+ // Auto-start for initial page (respect particles toggle)
requestAnimationFrame(() => {
+ if (window._particlesEnabled === false) return;
const activePage = document.querySelector('.page.active');
if (activePage) {
const pageId = activePage.id.replace('-page', '');
diff --git a/webui/static/script.js b/webui/static/script.js
index 36a8846b..71e3d755 100644
--- a/webui/static/script.js
+++ b/webui/static/script.js
@@ -706,6 +706,23 @@ function applyAccentColor(hex) {
if (swatch) swatch.style.background = hex;
}
+function applyParticlesSetting(enabled) {
+ const canvas = document.getElementById('page-particles-canvas');
+ if (canvas) canvas.style.display = enabled ? '' : 'none';
+ if (window.pageParticles) {
+ if (enabled) {
+ const activePage = document.querySelector('.page.active');
+ if (activePage) {
+ window.pageParticles.setPage(activePage.id.replace('-page', ''));
+ }
+ } else {
+ window.pageParticles.stop();
+ }
+ }
+ window._particlesEnabled = enabled;
+ localStorage.setItem('soulsync-particles', String(enabled));
+}
+
function initAccentColorListeners() {
const presetSelect = document.getElementById('accent-preset');
const customGroup = document.getElementById('custom-color-group');
@@ -728,12 +745,27 @@ function initAccentColorListeners() {
applyAccentColor(customPicker.value);
});
}
+
+ // Particles toggle — apply immediately on change
+ const particlesCheckbox = document.getElementById('particles-enabled');
+ if (particlesCheckbox) {
+ particlesCheckbox.addEventListener('change', () => {
+ applyParticlesSetting(particlesCheckbox.checked);
+ });
+ }
}
// Bootstrap accent from localStorage instantly (prevents default-color flash)
(function() {
const saved = localStorage.getItem('soulsync-accent');
if (saved) applyAccentColor(saved);
+ // Bootstrap particles setting from localStorage
+ const particlesSaved = localStorage.getItem('soulsync-particles');
+ if (particlesSaved === 'false') {
+ window._particlesEnabled = false;
+ const canvas = document.getElementById('page-particles-canvas');
+ if (canvas) canvas.style.display = 'none';
+ }
})();
// ── Profile System ─────────────────────────────────────────────
@@ -1819,7 +1851,7 @@ function navigateToPage(pageId) {
loadPageData(pageId);
// Update page background particles
- if (window.pageParticles) window.pageParticles.setPage(pageId);
+ if (window.pageParticles && window._particlesEnabled !== false) window.pageParticles.setPage(pageId);
}
// REPLACE your old loadPageData function with this one:
@@ -4641,6 +4673,12 @@ async function loadSettingsData() {
if (vizSelect) vizSelect.value = vizType;
sidebarVisualizerType = vizType;
+ // Background particles toggle
+ const particlesEnabled = settings.ui_appearance?.particles_enabled !== false; // default true
+ const particlesCheckbox = document.getElementById('particles-enabled');
+ if (particlesCheckbox) particlesCheckbox.checked = particlesEnabled;
+ applyParticlesSetting(particlesEnabled);
+
// Populate Logging information (read-only)
document.getElementById('log-level-display').textContent = settings.logging?.level || 'INFO';
document.getElementById('log-path-display').textContent = settings.logging?.path || 'logs/app.log';
@@ -5383,7 +5421,8 @@ async function saveSettings(quiet = false) {
ui_appearance: {
accent_preset: document.getElementById('accent-preset')?.value || '#1db954',
accent_color: document.getElementById('accent-custom-color')?.value || '#1db954',
- sidebar_visualizer: document.getElementById('sidebar-visualizer-type')?.value || 'bars'
+ sidebar_visualizer: document.getElementById('sidebar-visualizer-type')?.value || 'bars',
+ particles_enabled: document.getElementById('particles-enabled')?.checked !== false
},
youtube: {
cookies_browser: document.getElementById('youtube-cookies-browser').value,