+
+
+ Exponential Volume
+ Use logarithmic volume curve for finer low-volume control
+
+
+
diff --git a/js/player.js b/js/player.js
index ccb7648..8f3ce90 100644
--- a/js/player.js
+++ b/js/player.js
@@ -9,7 +9,7 @@ import {
getTrackYearDisplay,
createQualityBadgeHTML,
} from './utils.js';
-import { queueManager, replayGainSettings, trackDateSettings } from './storage.js';
+import { queueManager, replayGainSettings, trackDateSettings, exponentialVolumeSettings } from './storage.js';
import { audioContextManager } from './audio-context.js';
export class Player {
@@ -99,8 +99,11 @@ export class Player {
scale = 1.0 / peak;
}
+ // Apply exponential volume curve if enabled
+ const curvedVolume = exponentialVolumeSettings.applyCurve(this.userVolume);
+
// Calculate effective volume
- const effectiveVolume = this.userVolume * scale;
+ const effectiveVolume = curvedVolume * scale;
// Apply to audio element
this.audio.volume = Math.max(0, Math.min(1, effectiveVolume));
diff --git a/js/settings.js b/js/settings.js
index 05f229b..b2e9010 100644
--- a/js/settings.js
+++ b/js/settings.js
@@ -25,6 +25,7 @@ import {
sidebarSectionSettings,
fontSettings,
monoAudioSettings,
+ exponentialVolumeSettings,
} from './storage.js';
import { audioContextManager, EQ_PRESETS } from './audio-context.js';
import { getButterchurnPresets } from './visualizers/butterchurn.js';
@@ -774,6 +775,17 @@ export function initializeSettings(scrobbler, player, api, ui) {
});
}
+ // Exponential Volume Toggle
+ const exponentialVolumeToggle = document.getElementById('exponential-volume-toggle');
+ if (exponentialVolumeToggle) {
+ exponentialVolumeToggle.checked = exponentialVolumeSettings.isEnabled();
+ exponentialVolumeToggle.addEventListener('change', (e) => {
+ exponentialVolumeSettings.setEnabled(e.target.checked);
+ // Re-apply current volume to use new curve
+ player.applyReplayGain();
+ });
+ }
+
// ========================================
// 16-Band Equalizer Settings
// ========================================
diff --git a/js/storage.js b/js/storage.js
index 08f651a..d7af400 100644
--- a/js/storage.js
+++ b/js/storage.js
@@ -860,6 +860,41 @@ export const monoAudioSettings = {
},
};
+export const exponentialVolumeSettings = {
+ STORAGE_KEY: 'exponential-volume-enabled',
+
+ isEnabled() {
+ try {
+ return localStorage.getItem(this.STORAGE_KEY) === 'true';
+ } catch {
+ return false;
+ }
+ },
+
+ setEnabled(enabled) {
+ localStorage.setItem(this.STORAGE_KEY, enabled ? 'true' : 'false');
+ },
+
+ // Apply exponential curve to linear volume (0-1)
+ // Uses a power curve: output = input^3 for more natural volume control
+ applyCurve(linearVolume) {
+ if (!this.isEnabled()) {
+ return linearVolume;
+ }
+ // Exponential curve: cubed for much finer low-volume control
+ // This creates a more dramatic difference that you'll actually notice
+ return Math.pow(linearVolume, 3);
+ },
+
+ // Convert from perceived volume back to linear for UI
+ inverseCurve(perceivedVolume) {
+ if (!this.isEnabled()) {
+ return perceivedVolume;
+ }
+ return Math.cbrt(perceivedVolume);
+ },
+};
+
export const sidebarSettings = {
STORAGE_KEY: 'monochrome-sidebar-collapsed',