WaveformMeter
A row of animated accent bars that signals live audio, such as the user speaking or JARVIS answering. It is purely decorative and does not measure a real signal.
import { WaveformMeter } from 'jarvis-react-ui';
Active and inactive
The meter animates by default. Set active={false} when no audio flows: the bars shrink to dim 3px
stubs and the animation pauses, so the layout does not jump.
import { WaveformMeter } from 'jarvis-react-ui';
export default function ActiveInactive() {
return (
<>
<WaveformMeter />
<WaveformMeter active={false} />
</>
);
}
Bar count
barCount sets the number of bars (12 by default). Every bar gets its own animation delay, so
meters of any length ripple instead of moving in sync.
import { WaveformMeter } from 'jarvis-react-ui';
export default function BarCount() {
return (
<div className="flex items-end gap-8">
<WaveformMeter barCount={6} />
<WaveformMeter />
<WaveformMeter barCount={24} />
</div>
);
}
Mirrored
mirrored flips the meter horizontally. Use a normal and a mirrored meter on both sides of a label
or a PushToTalkButton for a symmetric layout.
import { WaveformMeter } from 'jarvis-react-ui';
export default function Mirrored() {
return (
<div className="flex items-center gap-4">
<WaveformMeter />
<span className="text-[10px] uppercase tracking-[2px] text-accent-bright">
Voice in
</span>
<WaveformMeter mirrored />
</div>
);
}
With the orb state
Drive active from the orb state: the meter usually runs while the state is listening or
speaking and rests otherwise. This demo cycles through the states on a timer; the
PushToTalkButton page shows a full voice turn.
import { useEffect, useState } from 'react';
import { StatusLabel, WaveformMeter } from 'jarvis-react-ui';
import type { AppOrbState } from 'jarvis-react-ui';
const CYCLE: AppOrbState[] = ['idle', 'listening', 'thinking', 'speaking', 'follow_up', 'working'];
export default function OrbStateCycle() {
const [index, setIndex] = useState(0);
const state = CYCLE[index % CYCLE.length] ?? 'idle';
const audible = state === 'listening' || state === 'speaking';
useEffect(() => {
const id = setInterval(() => setIndex((i) => i + 1), 1800);
return () => clearInterval(id);
}, []);
return (
<div className="flex items-center gap-6">
<WaveformMeter active={audible} />
<StatusLabel state={state} />
<WaveformMeter active={audible} mirrored />
</div>
);
}
For a smaller meter that sits inline with text, use WaveStrip.
Accessibility
- The meter is
aria-hidden: it carries no information a screen reader user needs. Announce the voice state in text, for example with StatusLabel. - The bars animate continuously while
active; turn the meter off when audio stops.
API
| Prop | Type | Default | Description |
|---|---|---|---|
active |
boolean |
true |
Whether the bars animate; when false they collapse to dim 3px stubs. |
barCount |
number |
12 |
Number of bars to render; every bar gets its own staggered animation delay. |
className |
string |
Extra classes for the root <div> element. |
|
mirrored |
boolean |
false |
Flips the meter horizontally, for the right-hand side of a symmetric layout. |