Skip to content
VoiceVoice orbMenu

Voice orb

A monochrome sphere of fine contour lines that carries the whole voice state machine through form and motion: idle, connecting, listening, thinking, speaking, interrupted, muted and error.

Pick a state and the orb morphs into it. Simulated voices, or your microphone.

No color, no glow. Lines facing you are darker and heavier and the far side shows through faintly, so the sphere reads as volume without a gradient. Each state has its own form, and the direction of the waves says who is talking: inward while the user speaks, outward while the agent replies. It reads with the sound off and in a screenshot.

Installation

$ pnpm dlx shadcn@latest add https://chatcn.dev/r/voice-orb.json

Usage

tsx
import { VoiceOrb } from "@/components/ui/chatcn/voice-orb"

<VoiceOrb state="listening" level={input.getLevel} />

Pass one level, or inputLevel and outputLevel when you meter both sides; the orb uses the input while listening and the output while speaking. Getters are read every frame without re-rendering. For demos without a microphone, simulate drives both sides with a built-in speech rhythm.

Examples

Every state

All eight states, each expressed by form and motion.

Sizes

40, 72, 144 and 224 px, or any number. The line count follows the size, from 5 lines at 40 px to 22 at 224 px, so small orbs stay crisp and large ones stay airy.

40, 72, 144 and 224 px, speaking.

SVG fallback

Where 2D canvas is missing, or with renderer="svg", the orb draws each state's form in SVG as a still frame. The level scales the whole sphere while listening and speaking.

Each state's form drawn in SVG, scaled by the level.

Reduced motion

With reduced motion on, every state is a still form: the partial trace, the waves, the flattened sphere, the dotted lines, the split. Nothing moves, and every state still reads.

A still form per state. Nothing moves.

States

StateFormMotion
idleA calm sphereBreathes every 4.6 s; the surface barely drifts
connectingLines traced in partThe traces chase around the sphere in a spiral
listeningWaves across the frontWaves travel inward; the level swells them
thinkingSmaller, turningA band of light sweeps down every 1.8 s, like the Thinking shimmer
speakingWaves across the frontWaves travel outward; the level swells the sphere
interruptedPressed flatWaves stop
mutedDotted, faintNone
errorSplit in two, faintNone

Props

PropTypeNotes
stateVoiceOrbStateDefault idle
levelnumber | () => number0 to 1. Used for both sides unless split
inputLevel, outputLevelnumber | () => numberPer side, when you meter both
simulatebooleanBuilt-in speech levels wherever no level is passed
size"sm" | "md" | "lg" | "xl" | number40, 72, 144, 224 px. Default lg
renderer"auto" | "canvas" | "svg"Default auto: canvas, SVG where canvas is missing
reducedMotionbooleanForce it on or off. Defaults to the user's setting
labelstringAccessible name. Defaults to Voice: and the state

Keyboard

The orb is not interactive on its own. Wrap it in a button to make it one, as VoiceSession does for interrupting.

Accessibility

  • role="img" with a name that follows the state, for example Voice: listening. Pair it with a visible state label; the orb never carries meaning alone.
  • Every state differs in form, not only in motion, so it reads under reduced motion and in still screenshots.
  • Colors come from the theme through currentColor, so text-* classes, dark mode and forced colors apply.

Motion

  • Every form parameter eases toward its target with a critically damped approach: no overshoot, no bounce, and any state can interrupt any other.
  • Levels use a fast attack and a slower release, so syllables swell and settle instead of flickering.
  • The loop pauses off screen and in hidden tabs, and sleeps once the form settles in the still states: interrupted, muted and error cost nothing.
  • A frame is a handful of strokes: lines are grouped by depth, so the sphere draws in about ten canvas calls at any size.