Cap fullscreen pulse brightness

This commit is contained in:
lucast committed 2026-10-06 01:56:46 +02:00
1 parent d07221f600
commit ebd6fedd89
2 files changed
+29 -15

No files matched your search

@@ -148,6 +148,7 @@ function draw(now: number) {
const colors = palette.value ?? fallback const colors = palette.value ?? fallback
const dominant = toOkLab(colors.dominant) const dominant = toOkLab(colors.dominant)
const accent = toOkLab(colors.accent) const accent = toOkLab(colors.accent)
const maxPaletteLightness = Math.max(dominant[0], accent[0])
const motionTime = time * ANIMATION_SPEED const motionTime = time * ANIMATION_SPEED
for (let y = 0; y < height; y++) { for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) { for (let x = 0; x < width; x++) {
@@ -161,7 +162,7 @@ function draw(now: number) {
value * (1 - blend) + accent[channel] * blend, value * (1 - blend) + accent[channel] * blend,
) as [number, number, number] ) as [number, number, number]
const beat = shapeBeatPulse(beatEnergy.value) 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[1] *= 1 + beat * 0.02
lab[2] *= 1 + beat * 0.02 lab[2] *= 1 + beat * 0.02
const color = fromOkLab(lab) const color = fromOkLab(lab)
+27 -14
View File
@@ -109,7 +109,9 @@ export default defineNuxtPlugin((nuxtApp) => {
const mediaElement = (howl as HowlWithMediaNode)._sounds?.[0]?._node const mediaElement = (howl as HowlWithMediaNode)._sounds?.[0]?._node
if (!mediaElement) { if (!mediaElement) {
if (DEBUG_BEAT_ANALYSIS) if (DEBUG_BEAT_ANALYSIS)
console.warn("[beat analyzer] failed: Howler media element unavailable") console.warn(
"[beat analyzer] failed: Howler media element unavailable",
)
return return
} }
@@ -124,7 +126,9 @@ export default defineNuxtPlugin((nuxtApp) => {
elementWithCapture.webkitCaptureStream elementWithCapture.webkitCaptureStream
if (!capture) { if (!capture) {
if (DEBUG_BEAT_ANALYSIS) if (DEBUG_BEAT_ANALYSIS)
console.warn("[beat analyzer] failed: media capture unsupported") console.warn(
"[beat analyzer] failed: media capture unsupported",
)
return return
} }
@@ -132,7 +136,9 @@ export default defineNuxtPlugin((nuxtApp) => {
const stream = capture.call(mediaElement) const stream = capture.call(mediaElement)
if (stream.getAudioTracks().length === 0) { if (stream.getAudioTracks().length === 0) {
if (DEBUG_BEAT_ANALYSIS) 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 return
} }
analysisContext ??= new AudioContext() analysisContext ??= new AudioContext()
@@ -140,7 +146,7 @@ export default defineNuxtPlugin((nuxtApp) => {
void analysisContext.resume().catch(() => {}) void analysisContext.resume().catch(() => {})
analysisNode = analysisContext.createAnalyser() analysisNode = analysisContext.createAnalyser()
analysisNode.fftSize = 2048 analysisNode.fftSize = 2048
analysisNode.smoothingTimeConstant = 0.3 analysisNode.smoothingTimeConstant = 0.05
analysisSource = analysisContext.createMediaStreamSource(stream) analysisSource = analysisContext.createMediaStreamSource(stream)
analysisSource.connect(analysisNode) analysisSource.connect(analysisNode)
@@ -155,13 +161,17 @@ export default defineNuxtPlugin((nuxtApp) => {
if (!analysisNode) return if (!analysisNode) return
analysisNode.getByteFrequencyData(samples) analysisNode.getByteFrequencyData(samples)
const sampleRate = analysisContext!.sampleRate 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( const lastBin = Math.min(
samples.length - 1, samples.length - 1,
Math.ceil((130 * 2048) / sampleRate), Math.ceil((130 * 2048) / sampleRate),
) )
let bass = 0 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 bass /= Math.max(1, lastBin - firstBin + 1) * 255
if (previousBass === null) previousBass = bass if (previousBass === null) previousBass = bass
const rise = Math.max(0, bass - previousBass) const rise = Math.max(0, bass - previousBass)
@@ -178,15 +188,13 @@ export default defineNuxtPlugin((nuxtApp) => {
) )
const now = performance.now() const now = performance.now()
const isStrongThump = const isStrongThump =
rise >= strongThumpThreshold && now - lastBeatLogAt > thumpCooldown rise >= strongThumpThreshold &&
now - lastBeatLogAt > thumpCooldown
const riseDelta = rise - riseAverage const riseDelta = rise - riseAverage
riseAverage += riseDelta * 0.01 riseAverage += riseDelta * 0.01
riseVariance += (riseDelta * riseDelta - riseVariance) * 0.01 riseVariance += (riseDelta * riseDelta - riseVariance) * 0.01
if (isStrongThump) { if (isStrongThump) {
const thumpStrength = Math.min( const thumpStrength = Math.min(0.38, 0.1 + attack * 2.5)
0.38,
0.1 + attack * 2.5,
)
pulse = Math.max(pulse, thumpStrength) pulse = Math.max(pulse, thumpStrength)
if (DEBUG_BEAT_ANALYSIS) { if (DEBUG_BEAT_ANALYSIS) {
console.log( console.log(
@@ -247,7 +255,10 @@ export default defineNuxtPlugin((nuxtApp) => {
album: currentSong.value.album.name, album: currentSong.value.album.name,
artwork: [ artwork: [
{ {
src: GET_FILE(currentSong.value.album.file, mediaBaseUrl), src: GET_FILE(
currentSong.value.album.file,
mediaBaseUrl,
),
sizes: "512x512", sizes: "512x512",
}, },
], ],
@@ -480,7 +491,8 @@ export default defineNuxtPlugin((nuxtApp) => {
event.ctrlKey || event.ctrlKey ||
event.metaKey || event.metaKey ||
event.altKey event.altKey
) return )
return
const target = event.target const target = event.target
if ( if (
@@ -488,7 +500,8 @@ export default defineNuxtPlugin((nuxtApp) => {
target.closest( target.closest(
"input, textarea, select, button, [contenteditable='true'], [role='slider']", "input, textarea, select, button, [contenteditable='true'], [role='slider']",
) )
) return )
return
switch (event.code) { switch (event.code) {
case "Space": case "Space":