Character Components
EstuaryCharacter
MonoBehaviour -- Represents a single AI character. Attach one per character in your scene.
Namespace: Estuary
Inspector Fields
| Field | Type | Default | Description |
|---|---|---|---|
CharacterId | string | "" | The character's ID from the Estuary Dashboard |
PlayerId | string | "" | Player identifier. If blank, defaults to player_<first 8 chars of device ID> |
AutoConnect | bool | true | Connect automatically on Start() |
AutoReconnect | bool | true | Reconnect automatically if the connection drops unexpectedly |
AudioSource | EstuaryAudioSource | — | Optional audio source for TTS playback |
Microphone | EstuaryMicrophone | — | Optional microphone component for voice input |
AutoStartVoiceSession | bool | false | Start a voice session after connecting |
Properties
| Property | Type | Access | Description |
|---|---|---|---|
CharacterId | string | get/set | Character ID |
PlayerId | string | get/set | Player ID |
AutoConnect | bool | get/set | Whether to connect on Start() |
AutoStartVoiceSession | bool | get/set | Whether to start a voice session after connecting |
IsConnected | bool | get | true when this character's session is active |
CurrentSession | SessionInfo | get | Session info after connecting (null before) |
IsVoiceSessionActive | bool | get | true when a voice session is running |
CurrentPartialResponse | string | get | Accumulated streaming response text |
CurrentMessageId | string | get | The message ID currently being processed |
Methods
| Method | Returns | Description |
|---|---|---|
Connect() | void | Make this character active and start the connection |
Disconnect() | void | Disconnect from the server |
SendText(string message) | void | Fire-and-forget wrapper for SendTextAsync(string) |
SendText(string message, bool textOnly) | void | Fire-and-forget wrapper for SendTextAsync(string, bool) |
SendTextAsync(string message) | Task | Send a text message. Suppresses TTS automatically when no voice session is active. |
SendTextAsync(string message, bool textOnly) | Task | Send a text message; textOnly=true suppresses TTS for this turn |
SayLine(string text, bool textOnly = false) | void | Fire-and-forget wrapper for SayLineAsync |
SayLineAsync(string text, bool textOnly = false) | Task | Script the character to say a specific prewritten line via TTS |
StartVoiceSession() | void | Fire-and-forget wrapper for StartVoiceSessionAsync |
StartVoiceSessionAsync() | Task | Start a voice session (enables backend STT and starts the microphone) |
EndVoiceSession() | void | Fire-and-forget wrapper for EndVoiceSessionAsync |
EndVoiceSessionAsync() | Task | End the current voice session |
StreamAudioAsync(string audioBase64) | Task | Stream base64-encoded PCM audio (used internally by EstuaryMicrophone in WebSocket mode) |
Interrupt() | void | Stop local audio playback and clear partial response state |
C# Events
| Event | Signature | Description |
|---|---|---|
OnConnected | Action<SessionInfo> | Connection established |
OnDisconnected | Action | Connection lost |
OnBotResponse | Action<BotResponse> | Streaming text chunk received |
OnVoiceReceived | Action<BotVoice> | TTS audio chunk received |
OnTranscript | Action<SttResponse> | Speech-to-text result |
OnInterrupt | Action<InterruptData> | Server-confirmed interrupt received |
OnError | Action<string> | Error message |
OnConnectionStateChanged | Action<ConnectionState> | Connection state changed |
OnActionReceived | Action<AgentAction> | Typed client action received |
UnityEvents (Inspector)
These events are serialized and can be wired in the Inspector without code:
| Event | Parameter | Description |
|---|---|---|
onConnected | SessionInfo | Connection established |
onDisconnected | — | Connection lost |
onBotResponse | BotResponse | Bot response chunk received |
onVoiceReceived | BotVoice | TTS audio chunk received |
onTranscript | SttResponse | Transcript received |
onInterrupt | — | Interrupt received |
onError | string | Error message |
onActionReceived | AgentAction | Action parsed from response |
Default Player ID
When PlayerId is left blank, the SDK generates a default during Awake:
player_<first 8 characters of SystemInfo.deviceUniqueIdentifier>
This provides basic persistence across app sessions on the same device without requiring any setup.
EstuaryAudioSource
MonoBehaviour -- Plays streaming TTS audio from bot responses. Uses a fixed 30-second ring buffer for smooth, gap-free playback in WebSocket mode. In LiveKit mode, audio is handled by LiveKit's AudioStream and this component primarily tracks message IDs for interrupts and provides a unified Volume and interrupt API.
Namespace: Estuary
[RequireComponent(typeof(AudioSource))] -- a Unity AudioSource is added automatically.
Inspector Fields
| Field | Type | Default | Description |
|---|---|---|---|
AudioSource | AudioSource | auto | Unity AudioSource used for playback |
Volume | float | 1.0 | Playback volume (0–1) |
The ring buffer length is a fixed 30-second constant -- it is not Inspector-configurable. The streaming sample rate matches Unity's AudioSettings.outputSampleRate; incoming audio at a different rate is resampled automatically.
Properties
| Property | Type | Access | Description |
|---|---|---|---|
IsPlaying | bool | get | true while audio is playing |
BufferedSampleCount | int | get | Samples currently buffered for playback |
CurrentMessageId | string | get | Message ID of the audio currently playing |
Volume | float | get/set | Playback volume (also propagated to LiveKit's bot audio in LiveKit mode) |
Methods
| Method | Returns | Description |
|---|---|---|
MarkAudioComplete(string messageId) | void | Mark the final chunk when supplying raw PCM bytes |
RedactMessage(string messageId) | void | Clear matching queued audio without discarding a newer response |
EnqueueAudio(BotVoice voice) | void | Decode and write a bot voice chunk into the ring buffer |
EnqueueAudio(byte[] audioBytes, int sampleRate, string messageId = null) | void | Write raw PCM16 bytes into the ring buffer |
StopPlayback() | void | Stop playback and clear the buffer |
InterruptMessage(string messageId) | void | Stop playback only if the given messageId is currently playing |
ClearStreamingBuffer() | void | Clear pending audio without firing interrupt events |
SetLiveKitManager(ILiveKitVoiceManager manager) | void | Switch to LiveKit playback mode (audio routed via LiveKit) |
Configure(VoiceMode voiceMode, ILiveKitVoiceManager liveKitManager = null) | void | Configure the source for WebSocket or LiveKit mode |
Events
| Event | Signature | Description |
|---|---|---|
OnPlaybackStarted | Action | Audio playback began |
OnPlaybackComplete | Action | Audio playback finished (buffer drained) |
OnPlaybackInterrupted | Action | Playback was stopped (interrupt or StopPlayback) |
How It Works
WebSocket mode: Bot audio arrives as base64-encoded PCM16 chunks in bot_voice events. EstuaryManager routes them to the active character, which calls EnqueueAudio(BotVoice). The component:
- Decodes the base64 audio into PCM16 bytes (already done on
BotVoice) - Converts PCM16 bytes to float samples
- Resamples if the server sample rate differs from Unity's output rate
- Writes to a 30-second circular ring buffer
- Feeds samples to a streaming
AudioClipvia a PCM reader callback
The ring buffer ensures smooth playback even when network jitter causes irregular chunk delivery, and applies a short fade-out on underrun to avoid clicks.
LiveKit mode: Audio playback is handled by LiveKit's AudioStream, which creates its own AudioSource at runtime. EstuaryAudioSource still tracks the current message_id for interrupt handling and propagates Volume to the LiveKit voice manager.
Interrupt handling
When the server confirms that the user interrupted the bot, EstuaryCharacter stops
playback and clears the buffer. OnPlaybackInterrupted fires, and
EstuaryCharacter.OnInterrupt provides the server's interrupt details.
Rich results and model clips
See REST, rich results, and moderation for the new
OnDelegationUpdate, OnApiEndpointResult, OnModerationWarning, OnModerationFlag,
OnServerError, and OnBotSpeakingStateChanged events, message-scoped playback
completion, and EstuaryClipPlayer.