React SDK
voicerun-react is the React client library for connecting web applications to VoiceRun agents. It provides:
- a
VoiceRunProviderfor wiring up the client in React - a
useVoiceRunhook for connection, microphone, and playback state - a lower-level
WebSocketClientfor direct integrations - prebuilt UI components for agent call interfaces
The package replaces the older primvoices-react package.
Installation#
Install the core package:
npm install voicerun-react
Or with Yarn:
yarn add voicerun-react
If you want to use the packaged UI components such as AgentPanel, AuraVisualizer, BarVisualizer, AgentControlBar, or AgentStateIndicator, also install the optional peer dependencies:
npm install class-variance-authority clsx tailwind-merge motion lucide-react
Quick start#
Wrap the part of your app that needs voice features in VoiceRunProvider, then use useVoiceRun() inside child components.
import { VoiceRunProvider, useVoiceRun } from 'voicerun-react'; const config = { agentId: 'your-agent-id', environment: 'staging', logLevel: 'ERROR', }; function App() { return ( <VoiceRunProvider config={config} autoConnect={true}> <VoiceChat /> </VoiceRunProvider> ); } function VoiceChat() { const { connect, disconnect, startListening, stopListening, sendTextEvent, isConnected, isListening, isPlaying, audioStats, debugMessages, error, } = useVoiceRun(); return ( <div> <p>Connected: {isConnected ? 'Yes' : 'No'}</p> <p>Listening: {isListening ? 'Yes' : 'No'}</p> <p>Playing: {isPlaying ? 'Yes' : 'No'}</p> <p>Audio level: {audioStats ? `${Math.round(audioStats.level * 100)}%` : 'N/A'}</p> <p>Debug messages: {debugMessages.length}</p> {error && <p>Error: {error}</p>} <button onClick={connect}>Connect</button> <button onClick={disconnect}>Disconnect</button> <button onClick={startListening}>Start listening</button> <button onClick={stopListening}>Stop listening</button> <button onClick={() => sendTextEvent('Hello from React')}>Send text</button> </div> ); }
Connection behavior#
A few implementation details are important when integrating the SDK:
autoConnectconnects the WebSocket automatically on mount, but it does not automatically start microphone capture- microphone capture starts when you call
startListening() sendTextEvent()only works while connecteddisconnect()stops listening, clears queued audio playback, and closes the WebSocket
API reference#
VoiceRunProvider#
VoiceRunProvider creates a shared VoiceRun client and exposes state through React context.
Props#
| Prop | Type | Required | Description |
|---|---|---|---|
config | WebSocketClientConfig | Yes | VoiceRun client configuration |
autoConnect | boolean | No | Whether to connect automatically on mount |
children | ReactNode | Yes | React children that can access the hook |
useVoiceRun()#
The useVoiceRun() hook exposes connection controls, microphone controls, playback state, audio stats, debug messages, and errors.
Returned values#
| Property | Type | Description |
|---|---|---|
connect | () => Promise<void> | Opens the VoiceRun WebSocket session |
disconnect | () => void | Stops listening and disconnects the client |
startListening | () => Promise<void> | Starts microphone capture and audio streaming |
stopListening | () => void | Stops microphone capture |
sendTextEvent | (text: string) => void | Sends a text event over the active WebSocket session |
isConnected | boolean | Whether the socket is connected |
isListening | boolean | Whether microphone capture is active |
isPlaying | boolean | Whether playback audio is currently being rendered |
audioStats | AudioStats | null | Current audio level and speech-detection info |
debugMessages | DebugMessage[] | Debug events received from the backend |
error | string | null | Current error state |
AudioStats#
interface AudioStats { level: number; isSpeaking: boolean; isPlayback?: boolean; }
levelis normalized to a0-1rangeisSpeakingis derived from a simple level thresholdisPlaybackindicates whether the stats represent playback audio rather than microphone input
DebugMessage#
interface DebugMessage { type: string; turn: number; name: string; data: Record<string, unknown>; }
WebSocketClientConfig#
The provider and the lower-level WebSocketClient both use the same config shape.
| Property | Type | Required | Description |
|---|---|---|---|
agentId | string | Yes | VoiceRun agent ID |
functionId | string | No | Function identifier to target |
environment | string | No | Environment name used when requesting the call endpoint |
strategy | 'cascade' | 'sts' | No | Routing strategy |
logLevel | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | No | Client logging level |
serverUrl | string | No | Explicit WebSocket URL; if omitted, the client fetches routing from the API |
apiUrl | string | No | Base API URL, defaulting to https://api.voicerun.com |
customParameters | Record<string, string> | No | Extra custom parameters passed when starting the call |
canary | boolean | No | Adds canary=true to the WebSocket URL |
origin | 'debugger' | 'web' | No | Call origin metadata |
audioStatsInterval | number | No | Audio stats update interval in milliseconds |
How connection setup works#
If you do not provide serverUrl, the client first makes a request to:
POST /v1/agents/{agentId}/call?inputType=mic&environment={environment}
That returns the WebSocket URL and call parameters used to start the session.
Prebuilt UI components#
voicerun-react ships with optional components for building a voice-call style interface.
These components are exported from the package:
AgentPanelAgentControlBarAgentStateIndicatorAuraVisualizerBarVisualizerReactShaderToyuseBarAnimatoruseAuraAnimatorderiveAgentStatecn
AgentPanel#
AgentPanel is the highest-level packaged UI. It combines a state indicator, an aura visualizer, and a control bar.
import { VoiceRunProvider, AgentPanel } from 'voicerun-react'; function CallPage() { return ( <VoiceRunProvider config={{ agentId: 'your-agent-id', environment: 'staging' }}> <div style={{ height: 600 }}> <AgentPanel color="#1FD5F9" onDisconnect={() => console.log('call ended')} /> </div> </VoiceRunProvider> ); }
AgentPanel props
| Prop | Type | Description |
|---|---|---|
className | string | Optional wrapper classes |
onDisconnect | () => void | Called after disconnect |
color | `#${string}` | Accent color for the aura visualizer |
AgentControlBar#
AgentControlBar provides a start-call button when disconnected, and then shows:
- current agent status text
- a compact bar visualizer
- a microphone mute/unmute toggle
- an end-call button
When disconnected, its built-in "Start Call" action calls both connect() and startListening().
import { AgentControlBar } from 'voicerun-react'; <AgentControlBar variant="pill" onDisconnect={() => router.push('/')} />
AgentControlBar props
| Prop | Type | Description |
|---|---|---|
variant | 'default' | 'outline' | 'pill' | Visual style |
className | string | Optional wrapper classes |
onDisconnect | () => void | Called after disconnect |
AgentStateIndicator#
AgentStateIndicator renders a labeled status pill for the current agent state.
Supported states:
disconnectedconnectinglisteningthinkingspeaking
import { AgentStateIndicator, deriveAgentState } from 'voicerun-react'; <AgentStateIndicator state={deriveAgentState({ isConnected, isListening, isPlaying })} />
AuraVisualizer#
AuraVisualizer is a WebGL-based animated visualization that reacts to agent state and audio level.
import { AuraVisualizer, deriveAgentState } from 'voicerun-react'; <AuraVisualizer state={deriveAgentState({ isConnected, isListening, isPlaying })} audioLevel={audioStats?.level ?? 0} size="lg" shapeScale={2.0} color="#1FD5F9" themeMode="dark" />
BarVisualizer#
BarVisualizer displays an animated audio bar treatment that responds to state and live level changes.
import { BarVisualizer, deriveAgentState } from 'voicerun-react'; <BarVisualizer state={deriveAgentState({ isConnected, isListening, isPlaying })} audioLevel={audioStats?.level ?? 0} size="md" color="#22c55e" />
deriveAgentState()#
The package includes a helper to convert hook booleans into UI state.
import { deriveAgentState } from 'voicerun-react'; const state = deriveAgentState({ isConnected, isListening, isPlaying, });
Current mapping:
- not connected →
disconnected - connected and playing audio →
speaking - connected and listening →
listening - otherwise connected →
thinking
Lower-level client usage#
If you do not want to use React context, you can instantiate WebSocketClient directly.
import { WebSocketClient } from 'voicerun-react'; const client = new WebSocketClient({ agentId: 'your-agent-id', environment: 'staging', logLevel: 'INFO', }); client.setCallbacks({ onOpen: () => console.log('connected'), onClose: () => console.log('disconnected'), onListeningStart: () => console.log('mic on'), onListeningStop: () => console.log('mic off'), onAudioStart: () => console.log('playback started'), onAudioStop: () => console.log('playback stopped'), onAudioStats: (stats) => console.log(stats), onDebugMessage: (messages) => console.log(messages), }); await client.connect(); await client.startListening(); client.sendTextEvent('Hello from the browser');
Audio and transport behavior#
From the current source implementation:
- microphone audio is captured through the Web Audio API
- the client prefers
AudioWorklet, withScriptProcessorNodeas a fallback - captured audio is downsampled to
16kHz - outgoing audio is μ-law encoded before being sent over WebSocket
- playback audio is decoded and played back at
24kHz - the client maintains a playback queue and schedules audio ahead to reduce gaps
- audio level stats are sampled on an interval and exposed through
audioStats
The package also handles:
- debug event collection
clearandmarkevents in the audio stream- control-message based agent redirects
- restoration of the original agent target after disconnecting from a redirected session
Browser support#
The package relies on browser support for:
- WebSocket API
- Web Audio API
navigator.mediaDevices.getUserMediaAudioWorkletorScriptProcessorNode- WebGL for
AuraVisualizer
The current docs in the repo list support targets of:
- Chrome 74+
- Firefox 75+
- Safari 14.1+
- Edge 79+
