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.