feat(web): react wave and ambient visuals to live audio
ci / ts / ts (packages + web) (push) Failing after 2m2s

This commit is contained in:
mrleo1nid committed 2026-10-21 16:44:04 +03:00
1 parent 9223516547
commit 4c2286e70d
8 files changed
+144 -7

No files matched your search

+16 -1
View File
@@ -5,6 +5,7 @@ import { drawLiquid, drawOrb, step, type WaveState } from './draw';
export interface WaveButtonProps {
variant?: 'orb' | 'liquid';
playing?: boolean;
readAudio?: () => { bass: number; mid: number; treble: number };
bpm?: number;
energy?: number;
colors?: [string, string, string];
@@ -28,6 +29,8 @@ function prefersReducedMotion(): boolean {
*/
export function WaveButton(props: WaveButtonProps) {
const size = props.size ?? 320;
const audioRef = useRef(props.readAudio);
audioRef.current = props.readAudio;
const canvasRef = useRef<HTMLCanvasElement>(null);
const labelRef = useRef<HTMLDivElement>(null);
const wrapRef = useRef<HTMLDivElement>(null);
@@ -55,6 +58,7 @@ export function WaveButton(props: WaveButtonProps) {
const onMq = () => (st.current.reduced = prefersReducedMotion());
mq?.addEventListener?.('change', onMq);
let raf = 0;
let livePulse = 0;
let last = performance.now();
const loop = (now: number) => {
const dt = Math.min(0.05, (now - last) / 1000);
@@ -67,12 +71,23 @@ export function WaveButton(props: WaveButtonProps) {
cv.height = Math.round(S * dpr);
}
step(w, dt);
const audio = !w.reduced && w.playing ? audioRef.current?.() : undefined;
if (audioRef.current && !w.reduced) {
const target = audio ? Math.min(1, audio.bass * 0.7 + audio.mid * 0.2) : 0;
livePulse += (target - livePulse) * (1 - Math.exp(-dt * (target > livePulse ? 35 : 9)));
w.pulse = livePulse;
w.amp = audio ? Math.min(1, audio.mid * 0.8 + audio.treble * 0.7) : 0;
}
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
ctx.clearRect(0, 0, S, S);
const sc = 1 - w.press * 0.07 + w.hover * 0.02;
const r = w.size * 0.5 * sc;
const r = w.size * 0.5 * sc * (1 + w.pulse * 0.055);
const cx = S / 2 + w.mx * w.hover * 6;
const cy = S / 2 + w.my * w.hover * 6;
const ripple = audio ? w.amp * 0.025 * Math.sin(w.t * 13) : 0;
ctx.translate(cx, cy);
ctx.scale(1 + ripple, 1 - ripple);
ctx.translate(-cx, -cy);
if (w.variant === 'liquid') drawLiquid(ctx, cx, cy, r, w, w.t);
else drawOrb(ctx, cx, cy, r, w, w.t);
if (labelRef.current) labelRef.current.style.transform = `scale(${sc + w.pulse * 0.03})`;