React SDK

voicerun-react is the React client library for connecting web applications to VoiceRun agents. It provides:

  • a VoiceRunProvider for wiring up the client in React
  • a useVoiceRun hook for connection, microphone, and playback state
  • a lower-level WebSocketClient for 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:

  • autoConnect connects the WebSocket automatically on mount, but it does not automatically start microphone capture
  • microphone capture starts when you call startListening()
  • sendTextEvent() only works while connected
  • disconnect() 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#

PropTypeRequiredDescription
configWebSocketClientConfigYesVoiceRun client configuration
autoConnectbooleanNoWhether to connect automatically on mount
childrenReactNodeYesReact 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#

PropertyTypeDescription
connect() => Promise<void>Opens the VoiceRun WebSocket session
disconnect() => voidStops listening and disconnects the client
startListening() => Promise<void>Starts microphone capture and audio streaming
stopListening() => voidStops microphone capture
sendTextEvent(text: string) => voidSends a text event over the active WebSocket session
isConnectedbooleanWhether the socket is connected
isListeningbooleanWhether microphone capture is active
isPlayingbooleanWhether playback audio is currently being rendered
audioStatsAudioStats | nullCurrent audio level and speech-detection info
debugMessagesDebugMessage[]Debug events received from the backend
errorstring | nullCurrent error state

AudioStats#

interface AudioStats { level: number; isSpeaking: boolean; isPlayback?: boolean; }
  • level is normalized to a 0-1 range
  • isSpeaking is derived from a simple level threshold
  • isPlayback indicates 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.

PropertyTypeRequiredDescription
agentIdstringYesVoiceRun agent ID
functionIdstringNoFunction identifier to target
environmentstringNoEnvironment name used when requesting the call endpoint
strategy'cascade' | 'sts'NoRouting strategy
logLevel'DEBUG' | 'INFO' | 'WARN' | 'ERROR'NoClient logging level
serverUrlstringNoExplicit WebSocket URL; if omitted, the client fetches routing from the API
apiUrlstringNoBase API URL, defaulting to https://api.voicerun.com
customParametersRecord<string, string>NoExtra custom parameters passed when starting the call
canarybooleanNoAdds canary=true to the WebSocket URL
origin'debugger' | 'web'NoCall origin metadata
audioStatsIntervalnumberNoAudio 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:

  • AgentPanel
  • AgentControlBar
  • AgentStateIndicator
  • AuraVisualizer
  • BarVisualizer
  • ReactShaderToy
  • useBarAnimator
  • useAuraAnimator
  • deriveAgentState
  • cn

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

PropTypeDescription
classNamestringOptional wrapper classes
onDisconnect() => voidCalled 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

PropTypeDescription
variant'default' | 'outline' | 'pill'Visual style
classNamestringOptional wrapper classes
onDisconnect() => voidCalled after disconnect

AgentStateIndicator#

AgentStateIndicator renders a labeled status pill for the current agent state.

Supported states:

  • disconnected
  • connecting
  • listening
  • thinking
  • speaking
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, with ScriptProcessorNode as 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
  • clear and mark events 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.getUserMedia
  • AudioWorklet or ScriptProcessorNode
  • WebGL for AuraVisualizer

The current docs in the repo list support targets of:

  • Chrome 74+
  • Firefox 75+
  • Safari 14.1+
  • Edge 79+

reactsdkwebjavascript