VoiceOrbs

GPU shader · zero deps

Siri Sheet

Glass sphere with a luminous sheet of light inside; the rim refracts and splits color. Raw WebGL, zero dependencies.

Rendering cost: smooth on the gpu, uses more battery.

Preview

Idle

The demo cycles the five states. The mic makes the orb react to your voice while listening or speaking.

State

Customize

Color · Default
Size · 168px
Speed · 1×

A dot marks each default.

Get the code

The prompt bundles every file plus your current settings, set up for Generic.

Zero dependencies: copy the files and it just works.

Props

Every orb implements the same contract, so they are interchangeable: swap the import and keep the props.

state
'idle' | 'connecting' | 'listening' | 'thinking' | 'speaking' | 'error' | 'disabled'
Default: 'idle'
Assistant lifecycle state driving the animation. error and disabled are optional extensions.
size
number
Default: 168 (184 for Nebula Orb)
Diameter of the orb in pixels, also exposed as the --orb-size CSS variable.
speed
number
Default: 1
Animation speed multiplier, also exposed as the --orb-speed CSS variable.
colorFrom
string
Default: per-orb gradient start
Gradient start color (any CSS color), exposed as --orb-color-from.
colorTo
string
Default: per-orb gradient end
Gradient end color (any CSS color), exposed as --orb-color-to.
levelRef
RefObject<number>
Default: undefined
Live audio amplitude in 0..1 read every frame without re-render; a negative value falls back to the procedural animation.
label
string
Default: 'Assistant orb'
Accessible name announced by screen readers via aria-label.
className
string
Default: undefined
Extra class names merged onto the root element.
ref
Ref<HTMLDivElement>
Default: undefined
React 19 ref to the root element for measuring, animating or scrolling into view.

Wire it to audio

Drive state from your assistant lifecycle and pass a levelRef with the live amplitude. The bundled useAudioLevel hook opens the microphone and writes a 0..1 level into the ref every frame without re-rendering; while the value is negative the orb falls back to its procedural animation.

'use client';
import { useState } from 'react';
import type { OrbState } from '@/registry/lib/orb-state';
import { useAudioLevel } from '@/registry/lib/use-audio-level';
import { SiriSheet } from '@/registry/orbe/siri-sheet/siri-sheet';
export const Assistant = () => {
const [state, setState] = useState<OrbState>('idle');
const { levelRef } = useAudioLevel(state === 'listening');
return <SiriSheet state={state} levelRef={levelRef} />;
};

Using Vapi, ElevenLabs, LiveKit or OpenAI Realtime? The recipes map each provider to state and levelRef.