diff --git a/index.html b/index.html
index b7b9e98..fd69136 100644
--- a/index.html
+++ b/index.html
@@ -119,7 +119,9 @@
diff --git a/js/settings.js b/js/settings.js
index e321eaa..fdd4b4a 100644
--- a/js/settings.js
+++ b/js/settings.js
@@ -2110,6 +2110,23 @@ export function initializeSettings(scrobbler, player, api, ui) {
});
}
+ const visualizerDimmingSlider = document.getElementById('visualizer-dimming-slider');
+ const visualizerDimmingValue = document.getElementById('visualizer-dimming-value');
+ if (visualizerDimmingSlider && visualizerDimmingValue) {
+ const currentDimming = visualizerSettings.getDimAmount();
+ visualizerDimmingSlider.value = currentDimming;
+ visualizerDimmingValue.textContent = `${(currentDimming * 100).toFixed(0)}%`;
+
+ visualizerDimmingSlider.addEventListener('input', (e) => {
+ const newDimming = parseFloat(e.target.value);
+ visualizerSettings.setDimAmount(newDimming);
+ visualizerDimmingValue.textContent = `${(newDimming * 100).toFixed(0)}%`;
+ window.dispatchEvent(
+ new CustomEvent('visualizer-dim-change', { detail: { dimAmount: newDimming } })
+ );
+ });
+ }
+
// Visualizer Smart Intensity
const smartIntensityToggle = document.getElementById('smart-intensity-toggle');
if (smartIntensityToggle) {
diff --git a/js/storage.js b/js/storage.js
index c238f58..12e3ec6 100644
--- a/js/storage.js
+++ b/js/storage.js
@@ -763,6 +763,7 @@ export const visualizerSettings = {
MODE_KEY: 'visualizer-mode', // 'solid' or 'blended'
PRESET_KEY: 'visualizer-preset',
BUTTERCHURN_CYCLE_KEY: 'butterchurn-cycle-duration',
+ DIM_AMOUNT_KEY: 'visualizer-dim-amount',
getPreset() {
try {
@@ -828,6 +829,20 @@ export const visualizerSettings = {
localStorage.setItem(this.SMART_INTENSITY_KEY, enabled);
},
+ getDimAmount() {
+ try {
+ const val = localStorage.getItem(this.DIM_AMOUNT_KEY);
+ if (val === null) return 1.0;
+ return parseFloat(val);
+ } catch {
+ return 1.0;
+ }
+ },
+
+ setDimAmount(value) {
+ localStorage.setItem(this.DIM_AMOUNT_KEY, value);
+ },
+
// Butterchurn preset cycle duration in seconds
getButterchurnCycleDuration() {
try {
diff --git a/js/ui.js b/js/ui.js
index 8fb1cdd..45ffb28 100644
--- a/js/ui.js
+++ b/js/ui.js
@@ -120,6 +120,12 @@ export class UIRenderer {
window.addEventListener('theme-changed', () => {
this.updateGlobalTheme();
});
+
+ window.addEventListener('visualizer-dim-change', () => {
+ if (this.visualizer) {
+ this.visualizer.updateDimming();
+ }
+ });
}
// Helper for Heart Icon
diff --git a/js/visualizer.js b/js/visualizer.js
index e3f5f3d..2e58dae 100644
--- a/js/visualizer.js
+++ b/js/visualizer.js
@@ -51,6 +51,12 @@ export class Visualizer {
this._resizeBound = () => this.resize();
}
+ updateDimming() {
+ if (!this.canvas || !this.canvas.parentElement) return;
+ const dimAmount = visualizerSettings.getDimAmount();
+ this.canvas.parentElement.style.opacity = dimAmount.toString();
+ }
+
get activePreset() {
return this.presets[this.activePresetKey] || this.presets['lcd'];
}
@@ -150,6 +156,8 @@ export class Visualizer {
this.audioContext.resume();
}
+ this.updateDimming();
+
// Set canvas dimensions before preset init so WebGL framebuffers are created at correct size
this.resize();
window.addEventListener('resize', this._resizeBound);
diff --git a/styles.css b/styles.css
index 49ae84b..743497c 100644
--- a/styles.css
+++ b/styles.css
@@ -3483,13 +3483,21 @@ input:checked + .slider::before {
transition: background-image var(--transition);
}
-#visualizer-canvas {
+#visualizer-container {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
z-index: 0;
pointer-events: none;
+ transition: opacity 0.3s ease;
+}
+
+#visualizer-canvas {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
}
.fullscreen-cover-content {