fix(client): surface onAudioAlignment on WebRTC transport#789
fix(client): surface onAudioAlignment on WebRTC transport#789JoanDuarte wants to merge 1 commit into
Conversation
The WebRTCConnection DataReceived handler dropped every JSON message
of type "audio" because the audio bytes flow over LiveKit audio tracks
and dont need to be re-played from the data channel. But the same
JSON message carries the alignment metadata (chars, char_start_times_ms,
char_durations_ms), which got dropped along with it. As a result,
onAudioAlignment never fired on the WebRTC transport even though the
WebSocket transport surfaces alignment correctly via the same code path.
Route audio messages through handleMessage so VoiceConversation.handleAudio
can fire onAudioAlignment, then return without re-playing audio_base_64
(LiveKit still plays it via the audio track).
Verified end-to-end on @elevenlabs/react-native@1.0.1 over WebRTC:
per-chunk alignment now arrives with {chars, char_start_times_ms,
char_durations_ms} matching the WebSocket protocol shape, and per-word
karaoke transcript stays synced to agent speech end-to-end across a
multi-turn conversation.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Reviewed by Cursor Bugbot for commit 10b65aa. Configure here.
| if (message.type === "audio") { | ||
| if (isValidSocketEvent(message)) { | ||
| this.handleMessage(message); | ||
| } |
There was a problem hiding this comment.
Duplicate onAudio callback from two audio paths
Medium Severity
In browser WebRTC environments, handleAudio now fires twice per audio chunk — once from the server's data-channel message (newly routed through handleMessage), and again from the existing setupAudioCapture worklet which re-captures LiveKit track audio and also calls handleMessage with a synthetic audio event. Both carry audio_base_64, so onAudio subscribers receive duplicate audio data. Additionally, currentEventId ping-pongs between the server's event_id and the local sequential audioEventId, potentially breaking sendFeedback tracking.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 10b65aa. Configure here.
There was a problem hiding this comment.
What do you think @JoanDuarte? Is this a valid concern which needs to be addressed?
|
My initial reaction is that it's hard to align the timestamps in these events with the audio streaming over WebRTC. What's your experience in that regard? |
|
Confirming this on |


Problem
VoiceConversation.handleAudio()fires theonAudioAlignmentcallback when an audio event with alignment metadata arrives, but on the WebRTC transport this callback never fires.In
WebRTCConnection.ts, theRoomEvent.DataReceivedhandler drops the entire JSON message whenmessage.type === "audio", with the comment "Filter out audio messages for WebRTC - they're handled via audio tracks." That comment is true for the audio bytes (audio_base_64) — LiveKit's audio track plays them — but the alignment metadata (chars,char_start_times_ms,char_durations_ms) lives in the same message and gets dropped with it.Net effect: consumers using
onAudioAlignmenton@elevenlabs/react-nativeor any WebRTC-transport@elevenlabs/clientuser receive nothing. The Swift SDK exposes alignment correctly; the WebSocket transport exposes alignment correctly; only WebRTC drops it.Fix
Route audio messages through
handleMessage()soVoiceConversation.handleAudiocan fireonAudioAlignment, then return without further processing (since LiveKit audio tracks already handle playback).if (message.type === "audio") { + if (isValidSocketEvent(message)) { + this.handleMessage(message); + } return; }Verification
Tested end-to-end against a live
@elevenlabs/react-native@1.0.1session on iOS. Prior to the fixonAudioAlignmentnever fired on WebRTC; after the fix it fires per audio chunk with the expected{ chars, char_start_times_ms, char_durations_ms }payload, matching the WebSocket transport's behavior. Per-word karaoke transcript stays synced to agent speech end-to-end across a multi-turn conversation.Side effects audited
VoiceConversation.handleAudiodoes the following afteronAudioAlignment:onAudio(audio_base_64)if subscribed. Harmless on WebRTC for consumers who don't subscribe; if they do, raw bytes are what they opted into.currentEventIdandcanSendFeedback. Previously these didn't advance on WebRTC audio events, which was arguably a pre-existing bug (sendFeedbackcouldn't reference WebRTC event IDs).updateMode("speaking"). Already fires elsewhere on WebRTC via mode events; idempotent here.No behavior regression observed.
Changeset
Patch-level bump to
@elevenlabs/client. Changeset included.Note
Low Risk
Low risk: a small change to WebRTC data-channel message routing to ensure
audioevents reach existing handlers; main risk is unintended side effects from processing additionalaudiomessages (e.g., duplicateonAudiocallbacks) if downstream consumers subscribe.Overview
Fixes WebRTC conversations dropping
type: "audio"data-channel messages by routing them throughhandleMessage(when valid) before returning, soVoiceConversation.handleAudiocan emitonAudioAlignmentfrom the alignment metadata.Adds a patch changeset for
@elevenlabs/clientdocumenting the alignment callback fix on the WebRTC transport.Reviewed by Cursor Bugbot for commit 10b65aa. Bugbot is set up for automated code reviews on this repo. Configure here.