aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
739 lines (736 loc) • 27.5 kB
JavaScript
'use client';
import { jsxs, jsx } from 'react/jsx-runtime';
import { useReducedMotion } from '../../hooks/useReducedMotion.js';
import { motion } from 'framer-motion';
import { forwardRef, useState, useRef, useCallback, useEffect } from 'react';
import { useMotionPreference } from '../../hooks/useMotionPreference.js';
import '../../primitives/GlassCore.js';
import '../../primitives/glass/GlassAdvanced.js';
import { OptimizedGlassCore } from '../../primitives/OptimizedGlassCore.js';
import '../../primitives/glass/OptimizedGlassAdvanced.js';
import '../../primitives/MotionNative.js';
import '../../primitives/motion/MotionFramer.js';
import { useA11yId } from '../../utils/a11y.js';
import { createGlassStyle } from '../../utils/createGlassStyle.js';
import { useGlassSound } from '../../utils/soundDesign.js';
const defaultAudioSettings = {
volume: 0.8,
gain: 1.0,
bassBoost: 0,
trebleBoost: 0,
smoothing: 0.8,
fftSize: 256
};
const defaultVisualSettings = {
mode: "bars",
colorScheme: "rainbow",
particleCount: 100,
sensitivity: 1.0,
symmetry: false,
mirror: false
};
const colorSchemes = {
rainbow: ["#ff0000", "#ff8000", "#ffff00", "#80ff00", "#00ff00", "#00ff80", "#00ffff", "#0080ff", "#0000ff", "#8000ff"],
monochrome: ["var(--glass-white)", "#e0e0e0", "#c0c0c0", "#a0a0a0", "#808080", "#606060", "#404040", "#202020"],
neon: ["#ff00ff", "#ff0080", "#ff0040", "#ff8040", "#ffff40", "#80ff40", "#40ff40", "#40ff80", "#40ffff", "#4080ff"],
fire: ["#ffff00", "#ffcc00", "#ff9900", "#ff6600", "#ff3300", "#ff0000", "#cc0000", "#990000"],
ice: ["var(--glass-white)", "#e0f0ff", "#c0e0ff", "#a0d0ff", "#80c0ff", "#60b0ff", "#40a0ff", "#2090ff"],
galaxy: ["#1a1a2e", "#16213e", "#0f3460", "#533483", "#7209b7", "#a663cc", "#4cc9f0"]
};
const GlassMusicVisualizer = /*#__PURE__*/forwardRef(({
audioSource,
audioSettings = {},
visualSettings = {},
showControls = true,
showFrequencyDisplay = true,
showWaveform = true,
showSpectrum = true,
realTimeAnalysis = true,
enableInteraction = true,
enableRecording = false,
canvasWidth = 800,
canvasHeight = 400,
onAudioLoad,
onFrequencyData,
onBeatDetected,
className = "",
...props
}, ref) => {
useReducedMotion();
const [isPlaying, setIsPlaying] = useState(false);
const [isRecording, setIsRecording] = useState(false);
const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0);
const [frequencyData, setFrequencyData] = useState(new Uint8Array(128));
const [waveformData, setWaveformData] = useState(new Uint8Array(128));
const [beatIntensity, setBeatIntensity] = useState(0);
const [audioConfig, setAudioConfig] = useState({
...defaultAudioSettings,
...audioSettings
});
const [visualConfig, setVisualConfig] = useState({
...defaultVisualSettings,
...visualSettings
});
const canvasRef = useRef(null);
const audioRef = useRef(null);
const audioContextRef = useRef(null);
const analyserRef = useRef(null);
const sourceRef = useRef(null);
const animationFrameRef = useRef();
const particles = useRef([]);
useA11yId("glass-music-visualizer");
const {
shouldAnimate
} = useMotionPreference();
const {
play
} = useGlassSound();
// Initialize audio context and analyser
const initializeAudio = useCallback(async () => {
try {
if (!audioContextRef.current) {
audioContextRef.current = new (window.AudioContext || window.webkitAudioContext)();
}
const context = audioContextRef.current;
const analyser = context.createAnalyser();
analyser.fftSize = audioConfig.fftSize;
analyser.smoothingTimeConstant = audioConfig.smoothing;
analyserRef.current = analyser;
// Initialize frequency and waveform data arrays
const bufferLength = analyser.frequencyBinCount;
setFrequencyData(new Uint8Array(bufferLength));
setWaveformData(new Uint8Array(bufferLength));
// Connect audio source if provided
if (audioSource && typeof audioSource === "string") {
const audio = audioRef.current;
if (audio) {
audio.src = audioSource;
const source = context.createMediaElementSource(audio);
source.connect(analyser);
analyser.connect(context.destination);
sourceRef.current = source;
}
} else if (audioSource instanceof MediaStream) {
const source = context.createMediaStreamSource(audioSource);
source.connect(analyser);
analyser.connect(context.destination);
sourceRef.current = source;
}
} catch (error) {
console.error("Failed to initialize audio:", error);
}
}, [audioSource, audioConfig.fftSize, audioConfig.smoothing]);
// Beat detection algorithm
const detectBeat = useCallback(frequencyData => {
const bassRange = Math.floor(frequencyData.length * 0.1);
const midRange = Math.floor(frequencyData.length * 0.3);
let bassSum = 0;
let midSum = 0;
for (let i = 0; i < bassRange; i++) {
bassSum += frequencyData[i];
}
for (let i = bassRange; i < midRange; i++) {
midSum += frequencyData[i];
}
const bassAvg = bassSum / bassRange;
const midAvg = midSum / (midRange - bassRange);
const intensity = (bassAvg + midAvg) / 2 / 255;
setBeatIntensity(intensity);
if (intensity > 0.7) {
onBeatDetected?.(intensity);
}
return intensity;
}, [onBeatDetected]);
// Visualization rendering
const renderVisualization = useCallback(() => {
const canvas = canvasRef.current;
const analyser = analyserRef.current;
if (!canvas || !analyser) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
// Update data arrays
const frequencyArray = new Uint8Array(analyser.frequencyBinCount);
const waveformArray = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(frequencyArray);
analyser.getByteTimeDomainData(waveformArray);
setFrequencyData(frequencyArray);
setWaveformData(waveformArray);
onFrequencyData?.(frequencyArray);
// Detect beats
const beat = detectBeat(frequencyArray);
// Clear canvas
ctx.fillStyle = "rgba(var(--glass-color-black) / var(--glass-opacity-10))";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Get colors for current scheme
const colors = colorSchemes[visualConfig.colorScheme] || colorSchemes.rainbow;
switch (visualConfig.mode) {
case "bars":
renderBars(ctx, frequencyArray, colors, beat);
break;
case "wave":
renderWave(ctx, waveformArray, colors, beat);
break;
case "circular":
renderCircular(ctx, frequencyArray, colors, beat);
break;
case "spectrum":
renderSpectrum(ctx, frequencyArray, colors);
break;
case "particles":
renderParticles(ctx, frequencyArray, colors);
break;
case "ripples":
renderRipples(ctx, frequencyArray, colors, beat);
break;
}
if (realTimeAnalysis && isPlaying) {
animationFrameRef.current = requestAnimationFrame(renderVisualization);
}
}, [visualConfig, realTimeAnalysis, isPlaying, detectBeat, onFrequencyData]);
// Visualization modes
const renderBars = (ctx, data, colors, beat) => {
const barWidth = ctx.canvas.width / data.length;
const heightScale = visualConfig.sensitivity;
for (let i = 0; i < data.length; i++) {
const barHeight = data[i] / 255 * ctx.canvas.height * heightScale;
const colorIndex = Math.floor(i / data.length * colors.length);
// Add beat intensity to color brightness
const alpha = Math.max(0.3, beat);
ctx.fillStyle = colors[colorIndex] + Math.floor(alpha * 255).toString(16).padStart(2, "0");
ctx.fillRect(i * barWidth, ctx.canvas.height - barHeight, barWidth - 1, barHeight);
// Mirror effect
if (visualConfig.mirror) {
ctx.fillRect(i * barWidth, 0, barWidth - 1, barHeight);
}
}
};
const renderWave = (ctx, data, colors, beat) => {
ctx.lineWidth = 2 + beat * 3;
ctx.strokeStyle = colors[Math.floor(beat * colors.length)];
ctx.beginPath();
const sliceWidth = ctx.canvas.width / data.length;
let x = 0;
for (let i = 0; i < data.length; i++) {
const v = data[i] / 128.0 * visualConfig.sensitivity;
const y = v * ctx.canvas.height / 2;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
x += sliceWidth;
}
ctx.stroke();
};
const renderCircular = (ctx, data, colors, beat) => {
const centerX = ctx.canvas.width / 2;
const centerY = ctx.canvas.height / 2;
const radius = Math.min(centerX, centerY) * 0.7;
for (let i = 0; i < data.length; i++) {
const angle = i / data.length * Math.PI * 2;
const amplitude = data[i] / 255 * radius * visualConfig.sensitivity * 0.5;
const x1 = centerX + Math.cos(angle) * radius;
const y1 = centerY + Math.sin(angle) * radius;
const x2 = centerX + Math.cos(angle) * (radius + amplitude);
const y2 = centerY + Math.sin(angle) * (radius + amplitude);
const colorIndex = Math.floor(i / data.length * colors.length);
ctx.strokeStyle = colors[colorIndex];
ctx.lineWidth = 1 + beat * 2;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
};
const renderSpectrum = (ctx, data, colors, beat) => {
const imageData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
const pixels = imageData.data;
// Shift existing data left
for (let x = 0; x < ctx.canvas.width - 1; x++) {
for (let y = 0; y < ctx.canvas.height; y++) {
const sourceIndex = (y * ctx.canvas.width + x + 1) * 4;
const targetIndex = (y * ctx.canvas.width + x) * 4;
pixels[targetIndex] = pixels[sourceIndex];
pixels[targetIndex + 1] = pixels[sourceIndex + 1];
pixels[targetIndex + 2] = pixels[sourceIndex + 2];
pixels[targetIndex + 3] = pixels[sourceIndex + 3];
}
}
// Add new column
const x = ctx.canvas.width - 1;
for (let i = 0; i < data.length; i++) {
const y = Math.floor(i / data.length * ctx.canvas.height);
const intensity = data[i] / 255;
const colorIndex = Math.floor(intensity * colors.length);
const color = colors[colorIndex] || "var(--glass-white)";
const r = parseInt(color.slice(1, 3), 16);
const g = parseInt(color.slice(3, 5), 16);
const b = parseInt(color.slice(5, 7), 16);
const index = (y * ctx.canvas.width + x) * 4;
pixels[index] = r * intensity;
pixels[index + 1] = g * intensity;
pixels[index + 2] = b * intensity;
pixels[index + 3] = 255 * intensity;
}
ctx.putImageData(imageData, 0, 0);
};
const renderParticles = (ctx, data, colors, beat) => {
// Update existing particles
particles.current = particles.current.filter(particle => {
particle.x += particle.vx;
particle.y += particle.vy;
particle.life -= 0.01;
particle.vy += 0.1; // gravity
return particle.life > 0 && particle.x >= 0 && particle.x <= ctx.canvas.width && particle.y >= 0 && particle.y <= ctx.canvas.height;
});
// Create new particles based on frequency data
for (let i = 0; i < data.length; i += 4) {
if (particles.current.length < visualConfig.particleCount) {
const intensity = data[i] / 255;
if (intensity > 0.1) {
particles.current.push({
x: i / data.length * ctx.canvas.width,
y: ctx.canvas.height - intensity * ctx.canvas.height * 0.5,
vx: (Math.random() - 0.5) * 4,
vy: -Math.random() * intensity * 5,
size: intensity * 5 + 1,
color: colors[Math.floor(intensity * colors.length)],
life: 1.0
});
}
}
}
// Render particles
particles.current.forEach(particle => {
ctx.save();
ctx.globalAlpha = particle.life;
ctx.fillStyle = particle.color;
ctx.beginPath();
ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
});
};
const renderRipples = (ctx, data, colors, beat) => {
const centerX = ctx.canvas.width / 2;
const centerY = ctx.canvas.height / 2;
// Calculate average intensity
data.reduce((sum, val) => sum + val, 0) / data.length / 255;
// Draw ripples based on beat intensity
if (beat > 0.3) {
const rippleCount = 5;
for (let i = 0; i < rippleCount; i++) {
const radius = beat * 200 + i * 50;
const alpha = Math.max(0, 1 - radius / 300);
ctx.strokeStyle = colors[i % colors.length] + Math.floor(alpha * 255).toString(16).padStart(2, "0");
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
ctx.stroke();
}
}
// Add frequency-based elements
for (let i = 0; i < data.length; i += 8) {
const angle = i / data.length * Math.PI * 2;
const intensity = data[i] / 255;
const radius = intensity * 100 + 50;
const x = centerX + Math.cos(angle) * radius;
const y = centerY + Math.sin(angle) * radius;
ctx.fillStyle = colors[Math.floor(intensity * colors.length)];
ctx.beginPath();
ctx.arc(x, y, intensity * 5 + 1, 0, Math.PI * 2);
ctx.fill();
}
};
// Audio controls
const handlePlay = useCallback(async () => {
if (!audioContextRef.current) {
await initializeAudio();
}
if (audioRef.current) {
try {
await audioRef.current.play();
setIsPlaying(true);
renderVisualization();
play("play");
} catch (error) {
console.error("Failed to play audio:", error);
}
}
}, [initializeAudio, renderVisualization, play]);
const handlePause = useCallback(() => {
if (audioRef.current) {
audioRef.current.pause();
setIsPlaying(false);
if (animationFrameRef.current) {
cancelAnimationFrame(animationFrameRef.current);
}
play("pause");
}
}, [play]);
const handleStop = useCallback(() => {
if (audioRef.current) {
audioRef.current.pause();
audioRef.current.currentTime = 0;
setIsPlaying(false);
setCurrentTime(0);
if (animationFrameRef.current) {
cancelAnimationFrame(animationFrameRef.current);
}
play("stop");
}
}, [play]);
// Initialize on mount
useEffect(() => {
if (audioSource) {
initializeAudio();
}
return () => {
if (animationFrameRef.current) {
cancelAnimationFrame(animationFrameRef.current);
}
if (audioContextRef.current) {
audioContextRef.current.close();
}
};
}, [audioSource, initializeAudio]);
// Update canvas size
useEffect(() => {
const canvas = canvasRef.current;
if (canvas) {
canvas.width = canvasWidth;
canvas.height = canvasHeight;
}
}, [canvasWidth, canvasHeight]);
const Controls = () => jsxs("div", {
className: 'glass-flex glass-items-center space-x-4',
children: [jsx(motion.button, {
className: 'glass-p-2 glass-surface-blue hover:glass-surface-blue text-primary glass-radius-lg transition-colors',
whileHover: shouldAnimate ? {
scale: 1.1
} : {},
whileTap: shouldAnimate ? {
scale: 0.9
} : {},
onClick: isPlaying ? handlePause : handlePlay,
children: isPlaying ? "⏸️" : "▶️"
}), jsx(motion.button, {
className: 'glass-p-2 glass-surface-primary hover:glass-surface-primary text-primary glass-radius-lg transition-colors',
whileHover: shouldAnimate ? {
scale: 1.1
} : {},
whileTap: shouldAnimate ? {
scale: 0.9
} : {},
onClick: handleStop,
children: "\u23F9\uFE0F"
}), jsxs("div", {
className: 'glass-flex glass-items-center space-x-2',
children: [jsxs("span", {
className: 'glass-text-xs text-primary/60',
children: [Math.floor(currentTime / 60), ":", Math.floor(currentTime % 60).toString().padStart(2, "0")]
}), jsx("span", {
className: 'text-primary/40',
children: "/"
}), jsxs("span", {
className: 'glass-text-xs text-primary/60',
children: [Math.floor(duration / 60), ":", Math.floor(duration % 60).toString().padStart(2, "0")]
})]
}), jsxs("div", {
className: 'glass-flex glass-items-center space-x-2',
children: [jsx("span", {
className: 'glass-text-xs text-primary/80',
children: "Volume:"
}), jsx("input", {
type: "range",
min: "0",
max: "1",
step: "0.1",
value: audioConfig.volume,
onChange: e => {
const volume = parseFloat(e.target.value);
setAudioConfig(prev => ({
...prev,
volume
}));
if (audioRef.current) {
audioRef.current.volume = volume;
}
},
className: 'w-16 h-2 glass-surface-subtle/20 glass-radius-lg appearance-none cursor-pointer'
})]
})]
});
return jsxs(OptimizedGlassCore, {
ref: ref,
variant: "frosted",
className: `p-6 space-y-6 ${className}`,
...props,
children: [jsxs("div", {
className: "glass-flex glass-items-center glass-justify-between",
children: [jsxs("div", {
children: [jsx("h3", {
className: 'glass-text-xl font-semibold text-primary/90',
children: "Music Visualizer"
}), jsx("p", {
className: 'glass-text-sm text-primary/60',
children: "Real-time audio visualization and analysis"
})]
}), jsxs("div", {
className: 'glass-flex glass-items-center space-x-2',
children: [realTimeAnalysis && jsxs("div", {
className: 'glass-flex glass-items-center space-x-1 text-primary',
children: [jsx("div", {
className: 'w-2 h-2 glass-surface-green glass-radius-full animate-pulse'
}), jsx("span", {
className: "glass-text-xs",
children: "Live"
})]
}), isRecording && jsxs("div", {
className: 'glass-flex glass-items-center space-x-1 text-primary',
children: [jsx("div", {
className: 'w-2 h-2 glass-surface-red glass-radius-full animate-pulse'
}), jsx("span", {
className: "glass-text-xs",
children: "Recording"
})]
})]
})]
}), audioSource && typeof audioSource === "string" && jsx("audio", {
ref: audioRef,
src: audioSource,
onLoadedMetadata: () => {
if (audioRef.current) {
setDuration(audioRef.current.duration);
onAudioLoad?.(audioRef.current.duration);
}
},
onTimeUpdate: () => {
if (audioRef.current) {
setCurrentTime(audioRef.current.currentTime);
}
}
}), showControls && jsx(Controls, {}), jsxs("div", {
className: 'relative',
children: [jsx("canvas", {
ref: canvasRef,
width: canvasWidth,
height: canvasHeight,
className: `
w-full border border-white/20 rounded-lg bg-black/20
${enableInteraction ? "cursor-pointer" : ""}
`,
onClick: enableInteraction ? isPlaying ? handlePause : handlePlay : undefined
}), jsx("div", {
className: 'absolute glass-top-2 right-2',
children: jsx("div", {
className: 'w-4 h-4 glass-radius-full glass-surface-red',
style: {
opacity: beatIntensity,
transform: `scale(${1 + beatIntensity})`
}
})
})]
}), jsxs("div", {
className: 'glass-grid glass-grid-cols-1 md:grid-cols-2 glass-gap-6',
children: [jsxs("div", {
className: 'space-y-4',
children: [jsx("h4", {
className: 'glass-text-sm font-medium text-primary/80',
children: "Visualization"
}), jsxs("div", {
className: 'space-y-3',
children: [jsxs("div", {
children: [jsx("label", {
className: 'block glass-text-xs text-primary/70 mb-1',
children: "Mode"
}), jsxs("select", {
value: visualConfig.mode,
onChange: e => setVisualConfig(prev => ({
...prev,
mode: e.target.value
})),
className: 'glass-w-full glass-p-2 glass-surface-subtle/10 glass-border glass-border-white/20 glass-radius-lg text-primary/90 glass-text-sm',
children: [jsx("option", {
value: "bars",
children: "Frequency Bars"
}), jsx("option", {
value: "wave",
children: "Waveform"
}), jsx("option", {
value: "circular",
children: "Circular"
}), jsx("option", {
value: "spectrum",
children: "Spectrum"
}), jsx("option", {
value: "particles",
children: "Particles"
}), jsx("option", {
value: "ripples",
children: "Ripples"
})]
})]
}), jsxs("div", {
children: [jsx("label", {
className: 'block glass-text-xs text-primary/70 mb-1',
children: "Color Scheme"
}), jsxs("select", {
value: visualConfig.colorScheme,
onChange: e => setVisualConfig(prev => ({
...prev,
colorScheme: e.target.value
})),
className: 'glass-w-full glass-p-2 glass-surface-subtle/10 glass-border glass-border-white/20 glass-radius-lg text-primary/90 glass-text-sm',
children: [jsx("option", {
value: "rainbow",
children: "Rainbow"
}), jsx("option", {
value: "monochrome",
children: "Monochrome"
}), jsx("option", {
value: "neon",
children: "Neon"
}), jsx("option", {
value: "fire",
children: "Fire"
}), jsx("option", {
value: "ice",
children: "Ice"
}), jsx("option", {
value: "galaxy",
children: "Galaxy"
})]
})]
}), jsxs("div", {
children: [jsxs("label", {
className: 'block glass-text-xs text-primary/70 mb-1',
children: ["Sensitivity: ", visualConfig.sensitivity.toFixed(1)]
}), jsx("input", {
type: "range",
min: "0.1",
max: "3.0",
step: "0.1",
value: visualConfig.sensitivity,
onChange: e => setVisualConfig(prev => ({
...prev,
sensitivity: parseFloat(e.target.value)
})),
className: 'glass-w-full h-2 glass-surface-subtle/20 glass-radius-lg appearance-none cursor-pointer'
})]
})]
})]
}), jsxs("div", {
className: 'space-y-4',
children: [jsx("h4", {
className: 'glass-text-sm font-medium text-primary/80',
children: "Audio Settings"
}), jsxs("div", {
className: 'space-y-3',
children: [jsxs("div", {
children: [jsxs("label", {
className: 'block glass-text-xs text-primary/70 mb-1',
children: ["Smoothing: ", audioConfig.smoothing.toFixed(1)]
}), jsx("input", {
type: "range",
min: "0.0",
max: "1.0",
step: "0.1",
value: audioConfig.smoothing,
onChange: e => {
const smoothing = parseFloat(e.target.value);
setAudioConfig(prev => ({
...prev,
smoothing
}));
if (analyserRef.current) {
analyserRef.current.smoothingTimeConstant = smoothing;
}
},
className: 'glass-w-full h-2 glass-surface-subtle/20 glass-radius-lg appearance-none cursor-pointer'
})]
}), jsxs("div", {
children: [jsx("label", {
className: 'block glass-text-xs text-primary/70 mb-1',
children: "FFT Size"
}), jsxs("select", {
value: audioConfig.fftSize,
onChange: e => setAudioConfig(prev => ({
...prev,
fftSize: parseInt(e.target.value)
})),
className: 'glass-w-full glass-p-2 glass-surface-subtle/10 glass-border glass-border-white/20 glass-radius-lg text-primary/90 glass-text-sm',
children: [jsx("option", {
value: "128",
children: "128"
}), jsx("option", {
value: "256",
children: "256"
}), jsx("option", {
value: "512",
children: "512"
}), jsx("option", {
value: "1024",
children: "1024"
}), jsx("option", {
value: "2048",
children: "2048"
})]
})]
})]
})]
})]
}), showFrequencyDisplay && jsxs("div", {
className: `
p-3 rounded-lg border border-white/10
${createGlassStyle({
blur: "sm",
opacity: 0.6
}).background}
`,
children: [jsx("h4", {
className: 'glass-text-sm font-medium text-primary/80 mb-2',
children: "Frequency Analysis"
}), jsxs("div", {
className: "glass-grid glass-grid-cols-4 glass-gap-4 glass-text-sm",
children: [jsxs("div", {
children: [jsx("span", {
className: 'text-primary/60',
children: "Bass:"
}), jsxs("div", {
className: 'text-primary/90 font-medium',
children: [Math.round(frequencyData.slice(0, 8).reduce((a, b) => a + b, 0) / 8 / 255 * 100), "%"]
})]
}), jsxs("div", {
children: [jsx("span", {
className: 'text-primary/60',
children: "Mid:"
}), jsxs("div", {
className: 'text-primary/90 font-medium',
children: [Math.round(frequencyData.slice(8, 32).reduce((a, b) => a + b, 0) / 24 / 255 * 100), "%"]
})]
}), jsxs("div", {
children: [jsx("span", {
className: 'text-primary/60',
children: "Treble:"
}), jsxs("div", {
className: 'text-primary/90 font-medium',
children: [Math.round(frequencyData.slice(32).reduce((a, b) => a + b, 0) / (frequencyData.length - 32) / 255 * 100), "%"]
})]
}), jsxs("div", {
children: [jsx("span", {
className: 'text-primary/60',
children: "Beat:"
}), jsxs("div", {
className: 'text-primary/90 font-medium',
children: [Math.round(beatIntensity * 100), "%"]
})]
})]
})]
})]
});
});
GlassMusicVisualizer.displayName = "GlassMusicVisualizer";
export { GlassMusicVisualizer };
//# sourceMappingURL=GlassMusicVisualizer.js.map