Skip to main content
Controlled mode lets orb-ui work with custom realtime voice AI stacks without a dedicated provider adapter. Use it when your app already knows:
  • the current voice agent state
  • the current input or output volume
  • when the session starts and stops
OrbSignal is the boundary between your voice runtime and the visual layer. Keep provider-specific events on one side of that boundary and emit the same small contract to React:

Common sources

Controlled mode works well with:
  • WebRTC sessions
  • WebSocket voice streams
  • telephony systems
  • internal speech pipelines
  • provider SDK wrappers
  • experimental OpenAI Realtime or Gemini Live API prototypes

Normalize state once

Custom runtimes often expose more states than a voice visualization needs. Map them in one function so the rest of the UI does not depend on transport terminology.
Application-only states such as muted, transferring, or waiting for approval can remain visible in adjacent text while the orb uses the closest core lifecycle state.

Normalize both volume directions

volume, inputVolume, and outputVolume should be normalized from 0 to 1.
Prefer inputVolume for microphone activity and outputVolume for assistant playback. Themes can then respond to the side of the conversation that owns the turn:
If a source reports decibels, RMS, or an unbounded analyser value, shape it before clamping rather than assuming it is already linear. Test normal speech, background noise, silence, and loud input.

Keep session controls explicit

When your application already owns the call lifecycle, render the orb as a passive visualization and keep labeled controls beside it:
This keeps the animation from becoming a second, unlabeled control and makes permission or network errors easier to explain outside the canvas.

Build an adapter later

If the same runtime integration is used in several components or applications, wrap it in an adapter. The adapter should subscribe to runtime events, emit a complete signal snapshot, own start and stop behavior when appropriate, and release every listener and browser resource during cleanup. See the signal-based voice agent UI guide for the complete contract, state and volume normalization guidance, and a production-shaped adapter with cleanup.
Avoid emitting only the field that changed. A complete snapshot prevents a stale input level or error from leaking into the next state. Return the real unsubscribe callback so React remounts do not accumulate provider listeners.

Errors and cleanup

Translate runtime failures into state: 'error' with the original Error when possible, but keep user-facing recovery copy in the application. Microphone denial, transport timeout, and remote session rejection usually need different next steps. On stop or unmount, release any microphone tracks, analyser loops, audio contexts, media elements, WebRTC peer connections, WebSocket listeners, and provider subscriptions your integration created. The orb can only reflect lifecycle accuracy if the underlying runtime actually returns to idle.

Custom integration checklist

  • Provider events map to the six supported states in one place.
  • Input and output levels are separately normalized between 0 and 1.
  • Every subscription has a matching cleanup function.
  • Start and stop can be called again after an error.
  • Microphone permission and connection failures have visible recovery actions.
  • A text label communicates state without relying on motion or color.
For an end-to-end example, see the voice orb UI example. If your provider already has a first-party adapter, compare this setup with the adapter overview before maintaining a custom wrapper. Use the React voice agent UI guide for the interaction states, accessibility, and recovery patterns that sit above either approach.
Last modified on July 22, 2026