Agents
AudioVisualizer
BetaBeta — the props contract is close to final, but small breaking changes can still land.Levels-driven realtime audio visualizer offered in two usage versions - bars and line - sharing one export and one props contract.
Version
Composition
The preferred shape for composing the installed agent from its exported parts.
Root
Single-slot surface with no nested parts.
AudioVisualizerUsage versions are sibling registry items sharing one export and one props contract — install the Bars item with the command above; swapping versions later is an import-path change, no call site moves.
npx shadcn@latest add http://127.0.0.1:3000/r/refined/audio-visualizer.jsonUsage
"use client";import { useEffect, useState } from "react";import { AudioVisualizer } from "@/components/refined-ui/audio-visualizer";// Mastra voice sessions hand you the live MediaStream (e.g. from the voice provider's speaker/microphone// events). Sample it with an AnalyserNode into a rolling 0..1 window — the visualizer only ever sees levels,// so the same mapping serves the bars and line versions alike.const WINDOW_SIZE = 48;const TICK_MS = 80;function useStreamLevels(stream: MediaStream | null) { const [levels, setLevels] = useState<number[]>(() => Array.from({ length: WINDOW_SIZE }, () => 0)); useEffect(() => { if (!stream) return; const context = new AudioContext(); const analyser = context.createAnalyser(); analyser.fftSize = 1024; context.createMediaStreamSource(stream).connect(analyser); const samples = new Uint8Array(analyser.fftSize); const timer = setInterval(() => { analyser.getByteTimeDomainData(samples); let sum = 0; for (const sample of samples) { const centered = (sample - 128) / 128; sum += centered * centered; } const rms = Math.sqrt(sum / samples.length); setLevels((previous) => [...previous.slice(1), Math.min(1, rms * 4)]); }, TICK_MS); return () => { clearInterval(timer); void context.close(); }; }, [stream]); return levels;}export function Example({ voiceStream }: { voiceStream: MediaStream | null }) { const levels = useStreamLevels(voiceStream); return <AudioVisualizer levels={levels} />;}Raw code
Primary installed agent source (Bars version)
Component
src/registry/sources/refined/audio-visualizer.tsx
import type { CSSProperties } from "react";import type { AudioVisualizerProps } from "@/components/refined-ui/contracts";import { cn } from "@/components/refined-ui/lib/cn";import { skinSlot } from "@/components/refined-ui/skin";// BARS version of the audio-visualizer usage family (the default). audio-visualizer-line.tsx is the sibling// version: same `AudioVisualizer` export, same AudioVisualizerProps contract (contracts.ts), different rendering —// installing either gives you <AudioVisualizer />, and swapping versions is an import-path change only.// Levels-driven and runtime-agnostic: feed it any rolling 0..1 window (AnalyserNode RMS, runner audio stream, …).type AudioVisualizerLevelStyle = CSSProperties & Record<"--audio-visualizer-level", string>;function visibleAudioLevels(levels: readonly number[], points?: number) { const count = Math.max(1, Math.floor(points ?? levels.length)); return levels.slice(-count).map((level) => Math.min(1, Math.max(0, level)));}export function AudioVisualizer({ levels, points, className, ...props }: AudioVisualizerProps) { const visible = visibleAudioLevels(levels, points); return ( <div data-ui="agent" data-component="audio-visualizer" data-variant="bars" aria-hidden="true" className={cn( "flex h-7 w-44 shrink-0 items-center justify-end gap-0.5 overflow-hidden rounded-[var(--radius-control)] px-1", skinSlot("audio-visualizer", { variant: "bars" }), className, )} {...props} > {visible.map((level, index) => { const levelStyle: AudioVisualizerLevelStyle = { "--audio-visualizer-level": `${level}` }; return ( // biome-ignore lint/suspicious/noArrayIndexKey: bars are a rolling window of levels; their identity is purely positional. <span key={`${index}-${visible.length}`} className="flex h-full w-1 shrink-0 items-center"> <span className="block h-[calc(var(--audio-visualizer-level)*100%)] min-h-1 w-full rounded-full bg-primary/70 transition-[height] duration-[var(--duration-fast)] ease-[var(--ease-standard)]" style={levelStyle} /> </span> ); })} </div> );}