Voice Orb

The face of a voice agent: rings that swell with the voice, circle while thinking and ripple while speaking.

Listening
'use client';

import { useEffect, useState } from 'react';
import { DictationButton, MagnetTabs, VoiceOrb } from '@brand-studio/ui';
import type { VoiceState } from '@brand-studio/ui';

const states: VoiceState[] = ['idle', 'listening', 'thinking', 'speaking'];

export default function VoiceOrbDemo() {
  const [state, setState] = useState<VoiceState>('listening');
  const [mic, setMic] = useState(false);
  const [level, setLevel] = useState(0);
  // Without the microphone, a made-up voice drives the level.
  useEffect(() => {
    if (mic || state !== 'listening') { if (!mic) setLevel(0); return; }
    let frame = 0;
    const tick = (time: number) => { setLevel(Math.max(0, Math.sin(time / 180) * Math.sin(time / 530)) * 0.9); frame = requestAnimationFrame(tick); };
    frame = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(frame);
  }, [mic, state]);
  return (
    <div style={{ width: '100%', display: 'grid', gap: 28, justifyItems: 'center' }}>
      <VoiceOrb state={state} level={level} size={140} />
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 12, alignItems: 'center', justifyContent: 'center' }}>
        <div style={{ maxWidth: '100%', overflowX: 'auto' }}>
          <MagnetTabs items={states.map(value => ({ value, label: value[0].toUpperCase() + value.slice(1) }))} value={state} onValueChange={value => setState(value as VoiceState)} aria-label="Orb state" />
        </div>
        <DictationButton label="Use my microphone" listening={mic} onListeningChange={on => { setMic(on); if (on) setState('listening'); }} onLevel={setLevel} />
      </div>
    </div>
  );
}

Installation

pnpm add @brand-studio/ui

Usage

import { VoiceOrb } from '@brand-studio/ui';
<VoiceOrb state="listening" level={level} size={140} />

level runs from 0 to 1. Feed it the microphone's loudness while listening (DictationButton's onLevel gives you this) or the agent's voice while speaking. Idle breathes slowly, thinking circles and speaking ripples outward.

Accessibility

  • The orb is a status that says its state in words: Ready, Listening, Thinking or Speaking. label shows your own words instead.
  • The drawing is hidden from screen readers.
  • With reduced motion the rings still follow the level but nothing loops.

API Reference

PropTypeDefault
state?"idle" | "listening" | "speaking" | "thinking"'idle'
level?number0
size?number120
label?string
className?string''

Still, Deskhand and Hollis are fictional brands. The coffee photographs are original generated artwork. @brand-studio/ui is MIT licensed.