billowy-hairdresser-80384
04/21/2026, 9:26 AMtall-airline-16756
04/21/2026, 9:26 AMtall-airline-16756
04/21/2026, 9:26 AMuse_tts_aligned_transcript=True on AgentSession.
◦ Node: set voiceOptions.useTtsAlignedTranscript = true.
1. Use a TTS provider that returns word-level timings:
◦ ElevenLabs: enable sync_alignment=True in TTS settings.
◦ Cartesia: ensure SSML/alignment is enabled.
◦ Other providers may only provide sentence-level alignment.
1. Consume timing data in your agent:
◦ Implement the transcription_node which yields TimedString objects containing start_time and end_time (seconds relative to the agent’s turn). Use these to map words/phrases to audio playback time.
1. Handle interruptions or partial output:
◦ By default transcripts will be truncated to match actually spoken audio. If you need transcripts immediately (unsynced), set sync_transcription=False in TextOutputOptions.
If you’d like, tell me:
• Which SDK you’re using (Python/Node/frontend framework), and
• Which TTS provider you plan to use
— and I’ll give a brief code example for your stack.
Sources: Text and transcriptions | LiveKit Documentation | ElevenLabs TTS | LiveKit Documentation | Cartesia TTS | LiveKit Documentation, with interactive elementsbillowy-hairdresser-80384
04/21/2026, 9:27 AMtall-airline-16756
04/21/2026, 9:27 AMsync_alignment=True so word-level timings are returned with audio chunks.
◦ Cartesia: enable SSML/alignment features to get timing info.
• Stream audio in time-ordered chunks to the client:
◦ Send audio chunks (or a continuous audio stream) in the same order and timing they were generated so playback time matches transcript timestamps.
• Use the timing data to drive UI playback and highlighting:
◦ Consume TimedString entries (start_time, end_time in seconds relative to the agent turn) and map those to audio playback progress to highlight words/phrases.
• Keep a common timebase:
◦ Align your playback clock to the agent’s turn timebase used by TimedString. If you buffer or delay playback, offset timestamps accordingly.
• Handle interruptions and partial audio:
◦ By default transcripts are truncated to match actually spoken audio. If the agent is interrupted, stop playback and trim displayed transcript to the final timings.
• If you need immediate (unsynced) transcripts:
◦ Set sync_transcription=False in TextOutputOptions (transcripts will arrive as available but won’t match exact audio timing).
If you want, tell me which SDK (Python/Node/browser) and which TTS provider you’ll use and I’ll give a short code snippet showing how to consume audio chunks and TimedString timestamps together.
, with interactive elementsbillowy-hairdresser-80384
04/21/2026, 9:28 AMtall-airline-16756
04/21/2026, 9:29 AMrtc.AudioStream to capture short, time-ordered audio frames (lightweight) instead of heavier recording solutions like RecorderIO.
• Only enable TTS-aligned transcripts when you need word-level timing; sentence-level alignment is cheaper.
• Buffer only short snippets (e.g., 5–10s) if you don’t need full-session audio.
• Rely on Opus DTX (enabled by default) to reduce bandwidth and processing during silence.
• Scale horizontally: add more worker instances for large numbers of concurrent agents.
If helpful, tell me:
• which SDK (Python/Node/browser) you use, and
• how many concurrent sessions/agents you expect
—I’ll suggest a concrete capture pattern and an estimated resource strategy.
Sources: Text and transcriptions | LiveKit Documentation | Self-hosted deployments | LiveKit Documentation | Capturing the First Few Seconds of a Call for Processing | Bringing Zoom's end-to-end optimizations to WebRTC | LiveKit | Bringing Zoom's end-to-end optimizations to WebRTC | LiveKit, with interactive elementsbillowy-hairdresser-80384
04/21/2026, 9:29 AMtall-airline-16756
04/21/2026, 9:29 AMuse_tts_aligned_transcript=True on your AgentSession.
1. Choose a TTS provider that returns word timings
◦ ElevenLabs: enable sync_alignment=True.
◦ Cartesia: enable SSML/alignment features.
1. Consume timed transcript events
◦ Implement the transcription_node in your agent; it yields TimedString objects with start_time and end_time (seconds relative to the agent turn). Use those timestamps to drive UI highlighting against audio playback.
1. Capture audio efficiently
◦ Use rtc.AudioStream in on_track_subscribed to tap raw audio frames for short in-memory buffering/processing instead of full-session recording.
◦ For short snippets, buffer ~5–10s of audio in memory and process; avoid holding entire sessions unless necessary.
Lightweight capture pattern (concept)
1. on_track_subscribed -> create an rtc.AudioStream for that track.
2. Collect frames into a ring buffer sized for N seconds (e.g., 5–10s).
3. When you need to transcribe or align, send buffered frames to STT/TTS pipeline, then discard or persist as needed.
4. Use `TimedString.start_time`/`end_time` and map to playback clock (apply an offset if you buffer/delay playback).
Compute and scaling guidance
• Yes, processing all audio streams increases CPU/memory; load scales with concurrent active sessions and enabled components (STT, VAD, noise suppression, alignment).
• Example benchmark: running Silero VAD for 30 agents on a 4-core/8GB machine used ~3.8 cores and ~2.8GB memory — expect similar growth when running per-stream real-time processing.
• rtc.AudioStream is lightweight compared to `RecorderIO`; prefer it for per-track, low-overhead capture.
• Rely on Opus DTX (enabled by default) — it reduces bandwidth/processing during silence.
• For high concurrency, scale horizontally (add worker instances) and/or limit per-instance processing (short buffers, offload heavy STT jobs to dedicated workers).
Next step offer
• If you share expected concurrency and whether you’ll run VAD/STT inline or offload, I’ll give a concrete Python snippet for on_track_subscribed + an estimated resource plan., with interactive elementsbillowy-hairdresser-80384
04/21/2026, 9:30 AMtall-airline-16756
04/21/2026, 9:30 AMrtc.AudioStream in on_track_subscribed and keep a small rolling buffer (5–10s) rather than full-session buffering.
• Produce/receive TTS audio with word timings (enable use_tts_aligned_transcript=True on AgentSession and sync_alignment=True for ElevenLabs / enable Cartesia alignment).
• As audio is generated/streamed, consume TimedString objects from transcription_node (they include `start_time`/`end_time` relative to the agent turn). Map those timestamps to your playback clock.
• If you delay playback (buffering/encoding), apply the same offset to TimedString timestamps so UI highlights remain aligned.
• For long responses or final, high-quality audio files: stream for real-time UX and concurrently write audio to disk (or object storage) in chunks; once file is complete you can produce a final pass if you need absolute-precision alignment.
• Use incremental commits: process and emit transcript highlights as soon as matching audio chunks are available; only keep longer-term storage for post-processing or archival.
Practical pattern
1. on_track_subscribed -> create rtc.AudioStream.
2. Push frames into a ring buffer (5–10s).
3. Stream frames to your playback client as they arrive.
4. When TimedString events arrive, use their times + your playback offset to highlight words in real time.
5. Optionally persist audio chunks to disk/cloud in the background for any later reprocessing.
When full in-memory buffering is necessary
• You only need full in-memory buffering if you must perform a single offline pass that requires the entire raw session immediately (rare for live UX). Otherwise, chunked streaming + background persistence is far more efficient.
If you want, I can:
• Provide a short Python snippet showing on_track_subscribed using rtc.AudioStream + a ring buffer and consuming TimedString events, or
• Sketch a storage/worker architecture for high concurrency. Which would you prefer?, with interactive elementsbillowy-hairdresser-80384
04/21/2026, 9:32 AMtall-airline-16756
04/21/2026, 9:32 AMTimedString timestamps (with any playback offset) to highlight words in real time.
• RecorderIO/full-session recording: use RecorderIO if you need a server-side single-file recording of the entire session immediately. This uses more memory/IO than streaming chunks.
• In-memory full buffer (only if necessary): buffer the entire session in RAM if you require a single, immediate in-memory audio blob for post-processing — not recommended at scale.
How to keep transcript alignment while getting the complete file
1. Enable TTS-aligned transcripts (use_tts_aligned_transcript=True) so you receive TimedString events with `start_time`/`end_time`.
2. Stream audio frames via rtc.AudioStream in on_track_subscribed and:
◦ Write frames to a rolling file/append to an on-disk temp file or upload chunks to object storage.
◦ Also push frames to the client for live playback.
1. Use the same timebase for playback and TimedString timestamps. If you buffer/delay playback by X seconds, apply the same offset to timestamps when highlighting.
2. After the session ends (or recording completes), you’ll have a complete audio file assembled from chunks or RecorderIO output that matches the transcript timings you already used during playback.
When to choose which approach
• Low memory + real-time UX: stream chunks to disk/cloud + real-time mapping with TimedString.
• Need immediate single file server-side: RecorderIO or assemble chunks into a file at end.
• High concurrency: avoid full in-memory buffering; prefer chunked streaming and background assembly.
If you want, I can provide a short Python snippet showing:
• streaming frames to disk in on_track_subscribed with a ring buffer, and
• consuming TimedString events to drive UI highlights with offsets.
Which snippet would help you most?
, with interactive elements