A ring of neon bars rising and falling like an audio equalizer.
three @react-three/fiber @react-three/drei<Spectrum /> inside your own <Canvas>.import { useMemo, useRef } from 'react';
import { useFrame } from '@react-three/fiber';
import * as THREE from 'three';
import type { Mesh } from 'three';
const N = 44;
const RADIUS = 1.4;
export default function Spectrum({ scale = 1 }: { color?: string; scale?: number }) {
const refs = useRef<(Mesh | null)[]>([]);
const bars = useMemo(() => {
const c1 = new THREE.Color('#22e0ff');
const c2 = new THREE.Color('#8a5cff');
const c3 = new THREE.Color('#ff2fd0');
const tmp = new THREE.Color();
return Array.from({ length: N }, (_, i) => {
const a = (i / N) * Math.PI * 2;
const t = i / (N - 1);
if (t < 0.5) tmp.copy(c1).lerp(c2, t * 2);
else tmp.copy(c2).lerp(c3, (t - 0.5) * 2);
return { a, x: Math.cos(a) * RADIUS, z: Math.sin(a) * RADIUS, color: tmp.getStyle() };
});
}, []);
useFrame((state) => {
const t = state.clock.elapsedTime;
bars.forEach((b, i) => {
const m = refs.current[i];
if (!m) return;
const h = 0.3 + (Math.sin(t * 3 + b.a * 3) * 0.5 + 0.5) * 1.5;
m.scale.y = h;
m.position.y = 0;
});
});
return (
<group scale={scale} rotation={[0.5, 0, 0]}>
{bars.map((b, i) => (
<mesh key={i} ref={(el) => (refs.current[i] = el)} position={[b.x, 0, b.z]} rotation={[0, -b.a, 0]}>
<boxGeometry args={[0.08, 1, 0.08]} />
<meshBasicMaterial color={b.color} toneMapped={false} />
</mesh>
))}
</group>
);
}