Initial commit: party-mix-app with prefetch cache, audio preload optimizations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
109
apps/web/src/components/AudioBackground.tsx
Normal file
109
apps/web/src/components/AudioBackground.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { audioState } from '@/lib/audioState'
|
||||
|
||||
export default function AudioBackground() {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current
|
||||
if (!canvas) return
|
||||
const ctx = canvas.getContext('2d')
|
||||
if (!ctx) return
|
||||
|
||||
let rafId: number
|
||||
let smoothBass = 0
|
||||
let smoothMid = 0
|
||||
let dataBuf: Uint8Array<ArrayBuffer> | null = null
|
||||
|
||||
const resize = () => {
|
||||
canvas.width = window.innerWidth
|
||||
canvas.height = window.innerHeight
|
||||
}
|
||||
resize()
|
||||
window.addEventListener('resize', resize)
|
||||
|
||||
const draw = () => {
|
||||
rafId = requestAnimationFrame(draw)
|
||||
const W = canvas.width
|
||||
const H = canvas.height
|
||||
|
||||
let rawBass = 0
|
||||
let rawMid = 0
|
||||
|
||||
if (audioState.analyser && audioState.isPlaying) {
|
||||
const binCount = audioState.analyser.frequencyBinCount
|
||||
if (!dataBuf || dataBuf.length !== binCount) dataBuf = new Uint8Array(new ArrayBuffer(binCount))
|
||||
audioState.analyser.getByteFrequencyData(dataBuf)
|
||||
const bassEnd = Math.max(1, Math.ceil(binCount * 0.1))
|
||||
for (let i = 0; i < bassEnd; i++) rawBass = Math.max(rawBass, dataBuf[i] / 255)
|
||||
const midStart = bassEnd
|
||||
const midEnd = Math.ceil(binCount * 0.4)
|
||||
let midSum = 0
|
||||
for (let i = midStart; i < midEnd; i++) midSum += dataBuf[i] / 255
|
||||
rawMid = midSum / Math.max(1, midEnd - midStart)
|
||||
}
|
||||
|
||||
smoothBass += (rawBass - smoothBass) * 0.1
|
||||
smoothMid += (rawMid - smoothMid) * 0.07
|
||||
|
||||
const t = Date.now() / 4500
|
||||
const breathe = Math.sin(t) * 0.03 + Math.cos(t * 0.7) * 0.015
|
||||
|
||||
ctx.clearRect(0, 0, W, H)
|
||||
ctx.fillStyle = '#0a0a0f'
|
||||
ctx.fillRect(0, 0, W, H)
|
||||
|
||||
const diag = Math.hypot(W, H)
|
||||
const base = diag * 0.55
|
||||
|
||||
// Lime orb — bass driven, top-left
|
||||
const r1 = base * (0.75 + smoothBass * 0.55 + breathe)
|
||||
const a1 = 0.055 + smoothBass * 0.07
|
||||
const g1 = ctx.createRadialGradient(W * 0.18, H * 0.08, 0, W * 0.18, H * 0.08, r1)
|
||||
g1.addColorStop(0, `rgba(200,255,0,${a1})`)
|
||||
g1.addColorStop(0.45, `rgba(200,255,0,${a1 * 0.28})`)
|
||||
g1.addColorStop(1, 'rgba(200,255,0,0)')
|
||||
ctx.fillStyle = g1
|
||||
ctx.fillRect(0, 0, W, H)
|
||||
|
||||
// Pink orb — mid driven, bottom-right
|
||||
const r2 = base * (0.65 + smoothMid * 0.45 - breathe * 0.6)
|
||||
const a2 = 0.045 + smoothMid * 0.055
|
||||
const g2 = ctx.createRadialGradient(W * 0.82, H * 0.92, 0, W * 0.82, H * 0.92, r2)
|
||||
g2.addColorStop(0, `rgba(255,60,172,${a2})`)
|
||||
g2.addColorStop(0.45, `rgba(255,60,172,${a2 * 0.28})`)
|
||||
g2.addColorStop(1, 'rgba(255,60,172,0)')
|
||||
ctx.fillStyle = g2
|
||||
ctx.fillRect(0, 0, W, H)
|
||||
|
||||
// Purple orb — combined, center, fades in when playing
|
||||
const combined = smoothBass * 0.55 + smoothMid * 0.45
|
||||
if (combined > 0.005) {
|
||||
const r3 = base * (0.38 + combined * 0.32)
|
||||
const a3 = combined * 0.035
|
||||
const g3 = ctx.createRadialGradient(W * 0.5, H * 0.52, 0, W * 0.5, H * 0.52, r3)
|
||||
g3.addColorStop(0, `rgba(140,100,255,${a3})`)
|
||||
g3.addColorStop(1, 'rgba(140,100,255,0)')
|
||||
ctx.fillStyle = g3
|
||||
ctx.fillRect(0, 0, W, H)
|
||||
}
|
||||
}
|
||||
|
||||
draw()
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(rafId)
|
||||
window.removeEventListener('resize', resize)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
className="fixed inset-0 w-full h-full pointer-events-none"
|
||||
style={{ zIndex: 0 }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user