From ebd6fedd89f20ab12f8faf912b3e14cfa4e93790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20T=C3=A4kker?= Date: Tue, 6 Oct 2026 01:56:05 +0200 Subject: [PATCH] Cap fullscreen pulse brightness --- .../Player/Fullscreen/Background/Wash.vue | 3 +- app/plugins/player.ts | 41 ++++++++++++------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/app/components/Player/Fullscreen/Background/Wash.vue b/app/components/Player/Fullscreen/Background/Wash.vue index 15972bb..b86e80f 100644 --- a/app/components/Player/Fullscreen/Background/Wash.vue +++ b/app/components/Player/Fullscreen/Background/Wash.vue @@ -148,6 +148,7 @@ function draw(now: number) { const colors = palette.value ?? fallback const dominant = toOkLab(colors.dominant) const accent = toOkLab(colors.accent) + const maxPaletteLightness = Math.max(dominant[0], accent[0]) const motionTime = time * ANIMATION_SPEED for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { @@ -161,7 +162,7 @@ function draw(now: number) { value * (1 - blend) + accent[channel] * blend, ) as [number, number, number] const beat = shapeBeatPulse(beatEnergy.value) - lab[0] = Math.min(1, lab[0] + beat * 0.01) + lab[0] = Math.min(maxPaletteLightness, lab[0] + beat * 0.01) lab[1] *= 1 + beat * 0.02 lab[2] *= 1 + beat * 0.02 const color = fromOkLab(lab) diff --git a/app/plugins/player.ts b/app/plugins/player.ts index 7398a8f..01ce300 100644 --- a/app/plugins/player.ts +++ b/app/plugins/player.ts @@ -109,7 +109,9 @@ export default defineNuxtPlugin((nuxtApp) => { const mediaElement = (howl as HowlWithMediaNode)._sounds?.[0]?._node if (!mediaElement) { if (DEBUG_BEAT_ANALYSIS) - console.warn("[beat analyzer] failed: Howler media element unavailable") + console.warn( + "[beat analyzer] failed: Howler media element unavailable", + ) return } @@ -124,7 +126,9 @@ export default defineNuxtPlugin((nuxtApp) => { elementWithCapture.webkitCaptureStream if (!capture) { if (DEBUG_BEAT_ANALYSIS) - console.warn("[beat analyzer] failed: media capture unsupported") + console.warn( + "[beat analyzer] failed: media capture unsupported", + ) return } @@ -132,7 +136,9 @@ export default defineNuxtPlugin((nuxtApp) => { const stream = capture.call(mediaElement) if (stream.getAudioTracks().length === 0) { if (DEBUG_BEAT_ANALYSIS) - console.warn("[beat analyzer] failed: capture has no audio tracks") + console.warn( + "[beat analyzer] failed: capture has no audio tracks", + ) return } analysisContext ??= new AudioContext() @@ -140,7 +146,7 @@ export default defineNuxtPlugin((nuxtApp) => { void analysisContext.resume().catch(() => {}) analysisNode = analysisContext.createAnalyser() analysisNode.fftSize = 2048 - analysisNode.smoothingTimeConstant = 0.3 + analysisNode.smoothingTimeConstant = 0.05 analysisSource = analysisContext.createMediaStreamSource(stream) analysisSource.connect(analysisNode) @@ -155,13 +161,17 @@ export default defineNuxtPlugin((nuxtApp) => { if (!analysisNode) return analysisNode.getByteFrequencyData(samples) const sampleRate = analysisContext!.sampleRate - const firstBin = Math.max(1, Math.floor((45 * 2048) / sampleRate)) + const firstBin = Math.max( + 1, + Math.floor((45 * 2048) / sampleRate), + ) const lastBin = Math.min( samples.length - 1, Math.ceil((130 * 2048) / sampleRate), ) let bass = 0 - for (let bin = firstBin; bin <= lastBin; bin++) bass += samples[bin] + for (let bin = firstBin; bin <= lastBin; bin++) + bass += samples[bin] bass /= Math.max(1, lastBin - firstBin + 1) * 255 if (previousBass === null) previousBass = bass const rise = Math.max(0, bass - previousBass) @@ -178,15 +188,13 @@ export default defineNuxtPlugin((nuxtApp) => { ) const now = performance.now() const isStrongThump = - rise >= strongThumpThreshold && now - lastBeatLogAt > thumpCooldown + rise >= strongThumpThreshold && + now - lastBeatLogAt > thumpCooldown const riseDelta = rise - riseAverage riseAverage += riseDelta * 0.01 riseVariance += (riseDelta * riseDelta - riseVariance) * 0.01 if (isStrongThump) { - const thumpStrength = Math.min( - 0.38, - 0.1 + attack * 2.5, - ) + const thumpStrength = Math.min(0.38, 0.1 + attack * 2.5) pulse = Math.max(pulse, thumpStrength) if (DEBUG_BEAT_ANALYSIS) { console.log( @@ -247,7 +255,10 @@ export default defineNuxtPlugin((nuxtApp) => { album: currentSong.value.album.name, artwork: [ { - src: GET_FILE(currentSong.value.album.file, mediaBaseUrl), + src: GET_FILE( + currentSong.value.album.file, + mediaBaseUrl, + ), sizes: "512x512", }, ], @@ -480,7 +491,8 @@ export default defineNuxtPlugin((nuxtApp) => { event.ctrlKey || event.metaKey || event.altKey - ) return + ) + return const target = event.target if ( @@ -488,7 +500,8 @@ export default defineNuxtPlugin((nuxtApp) => { target.closest( "input, textarea, select, button, [contenteditable='true'], [role='slider']", ) - ) return + ) + return switch (event.code) { case "Space":