From fbb7bfc27dccc1fd1a7dfe579760ae3678ba512e Mon Sep 17 00:00:00 2001 From: sliterok <12751644+sliterok@users.noreply.github.com> Date: Wed, 11 Jun 2025 21:59:39 +0200 Subject: [PATCH 1/3] feat: fft ripple modes --- src/backend/pattern/fftMirrorRipple.ts | 73 +++++++++++++++++++++++++ src/backend/pattern/fftRandomRipple.ts | 76 ++++++++++++++++++++++++++ src/backend/pattern/fftRipple.ts | 5 +- src/backend/pattern/index.ts | 6 +- src/backend/pattern/musicRipple.ts | 52 ------------------ src/backend/telegram/settings.ts | 4 +- src/typings.ts | 3 +- tests/fftMirrorRipple.test.ts | 17 ++++++ tests/fftRandomRipple.test.ts | 20 +++++++ tests/musicRipple.test.ts | 19 ------- 10 files changed, 199 insertions(+), 76 deletions(-) create mode 100644 src/backend/pattern/fftMirrorRipple.ts create mode 100644 src/backend/pattern/fftRandomRipple.ts delete mode 100644 src/backend/pattern/musicRipple.ts create mode 100644 tests/fftMirrorRipple.test.ts create mode 100644 tests/fftRandomRipple.test.ts delete mode 100644 tests/musicRipple.test.ts diff --git a/src/backend/pattern/fftMirrorRipple.ts b/src/backend/pattern/fftMirrorRipple.ts new file mode 100644 index 0000000..32336e8 --- /dev/null +++ b/src/backend/pattern/fftMirrorRipple.ts @@ -0,0 +1,73 @@ +import { IColorGetter, IColorMapper } from 'src/typings' +import { callIndexedGetter } from './mappers' +import { pixelsCount, hueToColor } from '../shared' +import { settings } from 'src/settings' +import { audioState } from '../wsAudio' + +interface Ripple { + pos: number + radius: number + brightness: number + hue: number +} + +let ripples: Ripple[] = [] +let lastTime = Date.now() +let averages: number[] = [] +let hueShift = 0 +const attenuation = 0.9 +const speed = 60 + +function spawnRipple(bin: number, mag: number) { + const hue = (bin / audioState.bins.length) * 360 + hueShift + const pos = (bin / audioState.bins.length) * pixelsCount + ripples.push({ pos, radius: 1, brightness: Math.min(1, mag), hue }) + ripples.push({ pos: pixelsCount - pos, radius: 1, brightness: Math.min(1, mag), hue }) +} + +function update(time: number) { + const dt = (time - lastTime) * settings.effectSpeed + lastTime = time + hueShift = (hueShift + dt * 0.05) % 360 + const bins = audioState.bins + if (bins && bins.length) { + if (averages.length !== bins.length) averages = bins.slice() + for (let i = 0; i < bins.length; i++) { + const m = bins[i] + averages[i] = averages[i] * 0.9 + m * 0.1 + if (m > averages[i] * 1.5) spawnRipple(i, m) + } + } + ripples.forEach(r => { + r.radius += (speed * dt) / 1000 + r.brightness *= Math.pow(attenuation, dt / 16) + }) + ripples = ripples.filter(r => r.brightness > 0.05) +} + +export const getFftMirrorColor: IColorGetter = (index, time) => { + if (index === 0) update(time) + let r = 0 + let g = 0 + let b = 0 + for (const ripple of ripples) { + const dist = Math.abs(index - ripple.pos) + if (dist <= ripple.radius) { + const intensity = ripple.brightness * (1 - dist / ripple.radius) + const { r: rr, g: gg, b: bb } = hueToColor(ripple.hue).rgb() + r += rr * intensity + g += gg * intensity + b += bb * intensity + } + } + return [Math.min(255, Math.round(r)), Math.min(255, Math.round(g)), Math.min(255, Math.round(b))] +} + +export function resetFftMirror() { + ripples = [] + lastTime = Date.now() + averages = [] + hueShift = 0 +} + +export const fftMirrorMapper: IColorMapper = () => callIndexedGetter(getFftMirrorColor) diff --git a/src/backend/pattern/fftRandomRipple.ts b/src/backend/pattern/fftRandomRipple.ts new file mode 100644 index 0000000..aea1b90 --- /dev/null +++ b/src/backend/pattern/fftRandomRipple.ts @@ -0,0 +1,76 @@ +import { IColorGetter, IColorMapper } from 'src/typings' +import { callIndexedGetter } from './mappers' +import { pixelsCount, hueToColor } from '../shared' +import { settings } from 'src/settings' +import { audioState } from '../wsAudio' + +interface Ripple { + pos: number + radius: number + brightness: number + hue: number +} + +let ripples: Ripple[] = [] +let lastTime = Date.now() +let averages: number[] = [] +let hueShift = 0 +const attenuation = 0.9 +const speed = 60 + +function spawnRipple(bin: number, mag: number) { + const hue = (bin / audioState.bins.length) * 360 + hueShift + ripples.push({ + pos: Math.random() * pixelsCount, + radius: 1, + brightness: Math.min(1, mag), + hue, + }) +} + +function update(time: number) { + const dt = (time - lastTime) * settings.effectSpeed + lastTime = time + hueShift = (hueShift + dt * 0.05) % 360 + const bins = audioState.bins + if (bins && bins.length) { + if (averages.length !== bins.length) averages = bins.slice() + for (let i = 0; i < bins.length; i++) { + const m = bins[i] + averages[i] = averages[i] * 0.9 + m * 0.1 + if (m > averages[i] * 1.5) spawnRipple(i, m) + } + } + ripples.forEach(r => { + r.radius += (speed * dt) / 1000 + r.brightness *= Math.pow(attenuation, dt / 16) + }) + ripples = ripples.filter(r => r.brightness > 0.05) +} + +export const getFftRandomColor: IColorGetter = (index, time) => { + if (index === 0) update(time) + let r = 0 + let g = 0 + let b = 0 + for (const ripple of ripples) { + const dist = Math.abs(index - ripple.pos) + if (dist <= ripple.radius) { + const intensity = ripple.brightness * (1 - dist / ripple.radius) + const { r: rr, g: gg, b: bb } = hueToColor(ripple.hue).rgb() + r += rr * intensity + g += gg * intensity + b += bb * intensity + } + } + return [Math.min(255, Math.round(r)), Math.min(255, Math.round(g)), Math.min(255, Math.round(b))] +} + +export function resetFftRandom() { + ripples = [] + lastTime = Date.now() + averages = [] + hueShift = 0 +} + +export const fftRandomMapper: IColorMapper = () => callIndexedGetter(getFftRandomColor) diff --git a/src/backend/pattern/fftRipple.ts b/src/backend/pattern/fftRipple.ts index df5ca29..ba9dea5 100644 --- a/src/backend/pattern/fftRipple.ts +++ b/src/backend/pattern/fftRipple.ts @@ -14,11 +14,12 @@ interface Ripple { let ripples: Ripple[] = [] let lastTime = Date.now() let averages: number[] = [] +let hueShift = 0 const attenuation = 0.9 const speed = 60 function spawnRipple(bin: number, mag: number) { - const hue = (bin / audioState.bins.length) * 270 + const hue = (bin / audioState.bins.length) * 360 + hueShift ripples.push({ pos: (bin / audioState.bins.length) * pixelsCount, radius: 1, @@ -30,6 +31,7 @@ function spawnRipple(bin: number, mag: number) { function update(time: number) { const dt = (time - lastTime) * settings.effectSpeed lastTime = time + hueShift = (hueShift + dt * 0.05) % 360 const bins = audioState.bins if (bins && bins.length) { if (averages.length !== bins.length) averages = bins.slice() @@ -68,6 +70,7 @@ export function resetFftRipples() { ripples = [] lastTime = Date.now() averages = [] + hueShift = 0 } export const fftRippleMapper: IColorMapper = () => callIndexedGetter(getFftRippleColor) diff --git a/src/backend/pattern/index.ts b/src/backend/pattern/index.ts index 7168c85..07684e9 100644 --- a/src/backend/pattern/index.ts +++ b/src/backend/pattern/index.ts @@ -9,8 +9,9 @@ import { getBreatheColor } from './breathe' import { getWaveColor } from './wave' import { getHeartbeatColor, getStrobeColor, getPulseColor, getGradientPulseColor, getMultiPulseColor } from './extra' import { rippleMapper } from './ripple' -import { musicRippleMapper } from './musicRipple' import { fftRippleMapper } from './fftRipple' +import { fftRandomMapper } from './fftRandomRipple' +import { fftMirrorMapper } from './fftMirrorRipple' import { createIndexedMapper, createFlatMapper } from './mappers' const transitionDuration = 250 @@ -39,8 +40,9 @@ const mappers: Record = { [IMode.GradientPulse]: createIndexedMapper(getGradientPulseColor), [IMode.MultiPulse]: createIndexedMapper(getMultiPulseColor), [IMode.Ripple]: rippleMapper, - [IMode.MusicRipple]: musicRippleMapper, [IMode.FftRipple]: fftRippleMapper, + [IMode.FftRandomRipple]: fftRandomMapper, + [IMode.FftMirrorRipple]: fftMirrorMapper, } export function getPixels(mode: IMode): IArrColor[][] { diff --git a/src/backend/pattern/musicRipple.ts b/src/backend/pattern/musicRipple.ts deleted file mode 100644 index 77edf1c..0000000 --- a/src/backend/pattern/musicRipple.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { IColorGetter, IColorMapper, IArrColor } from 'src/typings' -import { callIndexedGetter } from './mappers' -import { pixelsCount, hueToColor } from '../shared' -import { settings } from 'src/settings' -import { audioState } from '../wsAudio' - -interface Ripple { - pos: number - radius: number - speed: number - color: IArrColor -} - -let ripples: Ripple[] = [] -let lastTime = Date.now() - -function spawnRipple() { - const { r, g, b } = hueToColor(audioState.hue).rgb() - ripples.push({ pos: Math.random() * pixelsCount, radius: 1, speed: 50 + audioState.level * 200, color: [r, g, b] }) -} - -function update(time: number) { - const dt = (time - lastTime) * settings.effectSpeed - lastTime = time - if (audioState.level > 0.1) spawnRipple() - ripples.forEach(r => (r.radius += (r.speed * dt) / 1000)) - ripples = ripples.filter(r => r.radius < pixelsCount * 2) -} - -export const getMusicRippleColor: IColorGetter = (index, time) => { - if (index === 0) update(time) - let r = 0 - let g = 0 - let b = 0 - for (const ripple of ripples) { - const dist = Math.abs(index - ripple.pos) - const t = ripple.radius > 0 ? 1 - dist / ripple.radius : dist === 0 ? 1 : 0 - if (t > 0) { - r += ripple.color[0] * t - g += ripple.color[1] * t - b += ripple.color[2] * t - } - } - return [Math.min(255, Math.round(r)), Math.min(255, Math.round(g)), Math.min(255, Math.round(b))] -} - -export function resetMusicRipples() { - ripples = [] - lastTime = 0 -} - -export const musicRippleMapper: IColorMapper = () => callIndexedGetter(getMusicRippleColor) diff --git a/src/backend/telegram/settings.ts b/src/backend/telegram/settings.ts index 61af6d1..57c26e5 100644 --- a/src/backend/telegram/settings.ts +++ b/src/backend/telegram/settings.ts @@ -43,7 +43,9 @@ export function selectMode(menuTemplate: MenuTemplate) { [IMode.GradientPulse]: '🎇', [IMode.MultiPulse]: '🎆', [IMode.Ripple]: '💧', - [IMode.MusicRipple]: '🎶', + [IMode.FftRipple]: '🎶', + [IMode.FftRandomRipple]: '💿', + [IMode.FftMirrorRipple]: '🎵', }, { formatState, diff --git a/src/typings.ts b/src/typings.ts index 6f0827b..1ade0ad 100644 --- a/src/typings.ts +++ b/src/typings.ts @@ -26,8 +26,9 @@ export enum IMode { GradientPulse, MultiPulse, Ripple, - MusicRipple, FftRipple, + FftRandomRipple, + FftMirrorRipple, } export interface ISettings { diff --git a/tests/fftMirrorRipple.test.ts b/tests/fftMirrorRipple.test.ts new file mode 100644 index 0000000..4500b05 --- /dev/null +++ b/tests/fftMirrorRipple.test.ts @@ -0,0 +1,17 @@ +import { getFftMirrorColor, resetFftMirror } from '../src/backend/pattern/fftMirrorRipple' +import { audioState } from '../src/backend/wsAudio' + +beforeEach(() => { + resetFftMirror() + audioState.bins = Array(8).fill(0) +}) + +describe('fft mirror ripple', () => { + test('spawns ripple on spike', () => { + audioState.bins[0] = 2 + ;(getFftMirrorColor as any)(0, 0) + audioState.bins[0] = 10 + const color = (getFftMirrorColor as any)(0, 16) + expect(color).not.toEqual([0, 0, 0]) + }) +}) diff --git a/tests/fftRandomRipple.test.ts b/tests/fftRandomRipple.test.ts new file mode 100644 index 0000000..0460988 --- /dev/null +++ b/tests/fftRandomRipple.test.ts @@ -0,0 +1,20 @@ +import { getFftRandomColor, resetFftRandom } from '../src/backend/pattern/fftRandomRipple' +import { audioState } from '../src/backend/wsAudio' + +beforeEach(() => { + resetFftRandom() + audioState.bins = Array(8).fill(0) +}) + +describe('fft random ripple', () => { + test('spawns ripple on spike', () => { + const orig = Math.random + ;(Math as any).random = () => 0 + audioState.bins[0] = 2 + ;(getFftRandomColor as any)(0, 0) + audioState.bins[0] = 10 + const color = (getFftRandomColor as any)(0, 16) + expect(color).not.toEqual([0, 0, 0]) + Math.random = orig + }) +}) diff --git a/tests/musicRipple.test.ts b/tests/musicRipple.test.ts deleted file mode 100644 index 873ba62..0000000 --- a/tests/musicRipple.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { getMusicRippleColor, resetMusicRipples } from '../src/backend/pattern/musicRipple' -import { audioState } from '../src/backend/wsAudio' - -beforeEach(() => { - resetMusicRipples() - audioState.hue = 0 - audioState.level = 0 -}) - -describe('music ripple pattern', () => { - test('spawns ripple when level high', () => { - const orig = Math.random - ;(Math as any).random = () => 0 - audioState.level = 1 - audioState.hue = 0 - expect((getMusicRippleColor as any)(0, 0)).toEqual([255, 0, 0]) - Math.random = orig - }) -}) From 8948e8a9a3bc77c5abe418fb7a2f82f68ea6a87a Mon Sep 17 00:00:00 2001 From: sliterok <12751644+sliterok@users.noreply.github.com> Date: Wed, 11 Jun 2025 22:23:12 +0200 Subject: [PATCH 2/3] fix ripple colors --- src/backend/pattern/fftMirrorRipple.ts | 25 ++++++++++++++++++------- src/backend/pattern/fftRandomRipple.ts | 23 +++++++++++++++++------ src/backend/pattern/fftRipple.ts | 23 +++++++++++++++++------ 3 files changed, 52 insertions(+), 19 deletions(-) diff --git a/src/backend/pattern/fftMirrorRipple.ts b/src/backend/pattern/fftMirrorRipple.ts index 32336e8..73913ab 100644 --- a/src/backend/pattern/fftMirrorRipple.ts +++ b/src/backend/pattern/fftMirrorRipple.ts @@ -14,15 +14,18 @@ interface Ripple { let ripples: Ripple[] = [] let lastTime = Date.now() let averages: number[] = [] +let cooldowns: number[] = [] let hueShift = 0 const attenuation = 0.9 const speed = 60 +const spawnCooldown = 100 -function spawnRipple(bin: number, mag: number) { +function spawnRipple(bin: number, mag: number, avg: number) { const hue = (bin / audioState.bins.length) * 360 + hueShift + const brightness = Math.min(1, mag / (avg * 2)) const pos = (bin / audioState.bins.length) * pixelsCount - ripples.push({ pos, radius: 1, brightness: Math.min(1, mag), hue }) - ripples.push({ pos: pixelsCount - pos, radius: 1, brightness: Math.min(1, mag), hue }) + ripples.push({ pos, radius: 1, brightness, hue }) + ripples.push({ pos: pixelsCount - pos, radius: 1, brightness, hue }) } function update(time: number) { @@ -31,11 +34,18 @@ function update(time: number) { hueShift = (hueShift + dt * 0.05) % 360 const bins = audioState.bins if (bins && bins.length) { - if (averages.length !== bins.length) averages = bins.slice() + if (averages.length !== bins.length) { + averages = bins.slice() + cooldowns = new Array(bins.length).fill(0) + } for (let i = 0; i < bins.length; i++) { const m = bins[i] averages[i] = averages[i] * 0.9 + m * 0.1 - if (m > averages[i] * 1.5) spawnRipple(i, m) + cooldowns[i] -= dt + if (m > averages[i] * 1.5 && cooldowns[i] <= 0) { + spawnRipple(i, m, averages[i]) + cooldowns[i] = spawnCooldown + } } } ripples.forEach(r => { @@ -53,7 +63,7 @@ export const getFftMirrorColor: IColorGetter = (index, time) => { for (const ripple of ripples) { const dist = Math.abs(index - ripple.pos) if (dist <= ripple.radius) { - const intensity = ripple.brightness * (1 - dist / ripple.radius) + const intensity = (ripple.brightness * (1 - dist / ripple.radius)) / ripple.radius const { r: rr, g: gg, b: bb } = hueToColor(ripple.hue).rgb() r += rr * intensity g += gg * intensity @@ -65,8 +75,9 @@ export const getFftMirrorColor: IColorGetter = (index, time) => { export function resetFftMirror() { ripples = [] - lastTime = Date.now() + lastTime = 0 averages = [] + cooldowns = [] hueShift = 0 } diff --git a/src/backend/pattern/fftRandomRipple.ts b/src/backend/pattern/fftRandomRipple.ts index aea1b90..31230af 100644 --- a/src/backend/pattern/fftRandomRipple.ts +++ b/src/backend/pattern/fftRandomRipple.ts @@ -14,16 +14,19 @@ interface Ripple { let ripples: Ripple[] = [] let lastTime = Date.now() let averages: number[] = [] +let cooldowns: number[] = [] let hueShift = 0 const attenuation = 0.9 const speed = 60 +const spawnCooldown = 100 -function spawnRipple(bin: number, mag: number) { +function spawnRipple(bin: number, mag: number, avg: number) { const hue = (bin / audioState.bins.length) * 360 + hueShift + const brightness = Math.min(1, mag / (avg * 2)) ripples.push({ pos: Math.random() * pixelsCount, radius: 1, - brightness: Math.min(1, mag), + brightness, hue, }) } @@ -34,11 +37,18 @@ function update(time: number) { hueShift = (hueShift + dt * 0.05) % 360 const bins = audioState.bins if (bins && bins.length) { - if (averages.length !== bins.length) averages = bins.slice() + if (averages.length !== bins.length) { + averages = bins.slice() + cooldowns = new Array(bins.length).fill(0) + } for (let i = 0; i < bins.length; i++) { const m = bins[i] averages[i] = averages[i] * 0.9 + m * 0.1 - if (m > averages[i] * 1.5) spawnRipple(i, m) + cooldowns[i] -= dt + if (m > averages[i] * 1.5 && cooldowns[i] <= 0) { + spawnRipple(i, m, averages[i]) + cooldowns[i] = spawnCooldown + } } } ripples.forEach(r => { @@ -56,7 +66,7 @@ export const getFftRandomColor: IColorGetter = (index, time) => { for (const ripple of ripples) { const dist = Math.abs(index - ripple.pos) if (dist <= ripple.radius) { - const intensity = ripple.brightness * (1 - dist / ripple.radius) + const intensity = (ripple.brightness * (1 - dist / ripple.radius)) / ripple.radius const { r: rr, g: gg, b: bb } = hueToColor(ripple.hue).rgb() r += rr * intensity g += gg * intensity @@ -68,8 +78,9 @@ export const getFftRandomColor: IColorGetter = (index, time) => { export function resetFftRandom() { ripples = [] - lastTime = Date.now() + lastTime = 0 averages = [] + cooldowns = [] hueShift = 0 } diff --git a/src/backend/pattern/fftRipple.ts b/src/backend/pattern/fftRipple.ts index ba9dea5..efea712 100644 --- a/src/backend/pattern/fftRipple.ts +++ b/src/backend/pattern/fftRipple.ts @@ -14,16 +14,19 @@ interface Ripple { let ripples: Ripple[] = [] let lastTime = Date.now() let averages: number[] = [] +let cooldowns: number[] = [] let hueShift = 0 const attenuation = 0.9 const speed = 60 +const spawnCooldown = 100 -function spawnRipple(bin: number, mag: number) { +function spawnRipple(bin: number, mag: number, avg: number) { const hue = (bin / audioState.bins.length) * 360 + hueShift + const brightness = Math.min(1, mag / (avg * 2)) ripples.push({ pos: (bin / audioState.bins.length) * pixelsCount, radius: 1, - brightness: Math.min(1, mag), + brightness, hue, }) } @@ -34,11 +37,18 @@ function update(time: number) { hueShift = (hueShift + dt * 0.05) % 360 const bins = audioState.bins if (bins && bins.length) { - if (averages.length !== bins.length) averages = bins.slice() + if (averages.length !== bins.length) { + averages = bins.slice() + cooldowns = new Array(bins.length).fill(0) + } for (let i = 0; i < bins.length; i++) { const m = bins[i] averages[i] = averages[i] * 0.9 + m * 0.1 - if (m > averages[i] * 1.5) spawnRipple(i, m) + cooldowns[i] -= dt + if (m > averages[i] * 1.5 && cooldowns[i] <= 0) { + spawnRipple(i, m, averages[i]) + cooldowns[i] = spawnCooldown + } } } ripples.forEach(r => { @@ -56,7 +66,7 @@ export const getFftRippleColor: IColorGetter = (index, time) => { for (const ripple of ripples) { const dist = Math.abs(index - ripple.pos) if (dist <= ripple.radius) { - const intensity = ripple.brightness * (1 - dist / ripple.radius) + const intensity = (ripple.brightness * (1 - dist / ripple.radius)) / ripple.radius const { r: rr, g: gg, b: bb } = hueToColor(ripple.hue).rgb() r += rr * intensity g += gg * intensity @@ -68,8 +78,9 @@ export const getFftRippleColor: IColorGetter = (index, time) => { export function resetFftRipples() { ripples = [] - lastTime = Date.now() + lastTime = 0 averages = [] + cooldowns = [] hueShift = 0 } From b96231532b224083d267f8b6f21030a0906dd845 Mon Sep 17 00:00:00 2001 From: sliterok <12751644+sliterok@users.noreply.github.com> Date: Wed, 11 Jun 2025 22:36:16 +0200 Subject: [PATCH 3/3] fix: ripple color --- src/backend/pattern/fftMirrorRipple.ts | 10 +++++----- src/backend/pattern/fftRandomRipple.ts | 10 +++++----- src/backend/pattern/fftRipple.ts | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/backend/pattern/fftMirrorRipple.ts b/src/backend/pattern/fftMirrorRipple.ts index 73913ab..377c463 100644 --- a/src/backend/pattern/fftMirrorRipple.ts +++ b/src/backend/pattern/fftMirrorRipple.ts @@ -18,11 +18,11 @@ let cooldowns: number[] = [] let hueShift = 0 const attenuation = 0.9 const speed = 60 -const spawnCooldown = 100 +const spawnCooldown = 200 function spawnRipple(bin: number, mag: number, avg: number) { const hue = (bin / audioState.bins.length) * 360 + hueShift - const brightness = Math.min(1, mag / (avg * 2)) + const brightness = Math.min(1, Math.max(0, (mag - avg) / avg)) const pos = (bin / audioState.bins.length) * pixelsCount ripples.push({ pos, radius: 1, brightness, hue }) ripples.push({ pos: pixelsCount - pos, radius: 1, brightness, hue }) @@ -65,9 +65,9 @@ export const getFftMirrorColor: IColorGetter = (index, time) => { if (dist <= ripple.radius) { const intensity = (ripple.brightness * (1 - dist / ripple.radius)) / ripple.radius const { r: rr, g: gg, b: bb } = hueToColor(ripple.hue).rgb() - r += rr * intensity - g += gg * intensity - b += bb * intensity + r = Math.max(r, rr * intensity) + g = Math.max(g, gg * intensity) + b = Math.max(b, bb * intensity) } } return [Math.min(255, Math.round(r)), Math.min(255, Math.round(g)), Math.min(255, Math.round(b))] diff --git a/src/backend/pattern/fftRandomRipple.ts b/src/backend/pattern/fftRandomRipple.ts index 31230af..44e1e2d 100644 --- a/src/backend/pattern/fftRandomRipple.ts +++ b/src/backend/pattern/fftRandomRipple.ts @@ -18,11 +18,11 @@ let cooldowns: number[] = [] let hueShift = 0 const attenuation = 0.9 const speed = 60 -const spawnCooldown = 100 +const spawnCooldown = 200 function spawnRipple(bin: number, mag: number, avg: number) { const hue = (bin / audioState.bins.length) * 360 + hueShift - const brightness = Math.min(1, mag / (avg * 2)) + const brightness = Math.min(1, Math.max(0, (mag - avg) / avg)) ripples.push({ pos: Math.random() * pixelsCount, radius: 1, @@ -68,9 +68,9 @@ export const getFftRandomColor: IColorGetter = (index, time) => { if (dist <= ripple.radius) { const intensity = (ripple.brightness * (1 - dist / ripple.radius)) / ripple.radius const { r: rr, g: gg, b: bb } = hueToColor(ripple.hue).rgb() - r += rr * intensity - g += gg * intensity - b += bb * intensity + r = Math.max(r, rr * intensity) + g = Math.max(g, gg * intensity) + b = Math.max(b, bb * intensity) } } return [Math.min(255, Math.round(r)), Math.min(255, Math.round(g)), Math.min(255, Math.round(b))] diff --git a/src/backend/pattern/fftRipple.ts b/src/backend/pattern/fftRipple.ts index efea712..520e464 100644 --- a/src/backend/pattern/fftRipple.ts +++ b/src/backend/pattern/fftRipple.ts @@ -18,11 +18,11 @@ let cooldowns: number[] = [] let hueShift = 0 const attenuation = 0.9 const speed = 60 -const spawnCooldown = 100 +const spawnCooldown = 200 function spawnRipple(bin: number, mag: number, avg: number) { const hue = (bin / audioState.bins.length) * 360 + hueShift - const brightness = Math.min(1, mag / (avg * 2)) + const brightness = Math.min(1, Math.max(0, (mag - avg) / avg)) ripples.push({ pos: (bin / audioState.bins.length) * pixelsCount, radius: 1, @@ -68,9 +68,9 @@ export const getFftRippleColor: IColorGetter = (index, time) => { if (dist <= ripple.radius) { const intensity = (ripple.brightness * (1 - dist / ripple.radius)) / ripple.radius const { r: rr, g: gg, b: bb } = hueToColor(ripple.hue).rgb() - r += rr * intensity - g += gg * intensity - b += bb * intensity + r = Math.max(r, rr * intensity) + g = Math.max(g, gg * intensity) + b = Math.max(b, bb * intensity) } } return [Math.min(255, Math.round(r)), Math.min(255, Math.round(g)), Math.min(255, Math.round(b))]