Add live captions to a LiveKit voice room
Publish an audio room as a captioned vertical video without building a speech pipeline or a headless browser farm.
The pipeline
- LiveKit audio
- ASR node
- caption data events
- web capture overlay
- RTMP output
What this replaces
Build it yourself
- Forward every participant track to a speech service
- Align transcript events with speakers and timing
- Render captions into a vertical canvas
- Run and record a browser at production quality
With AVFlow
- Let the ASR node produce text events
- Send events to your LiveKit client or backend
- Render captions in your own web page
- Capture that page with web_capture and push it to RTMP
The validated Job
This is the same file 22-voice-room-captions.json serves. Replace platform credentials, output URLs, and storage secrets before running it.
{
"name": "voiceroom-captions",
"sources": [
{
"name": "room",
"type": "livekit",
"config": {
"serverUrl": "wss://your-project.livekit.cloud",
"token": "<subscribe-token>",
"select": {
"mediaTypes": [
"audio"
],
"excludeIdentities": [
"avflow-captions"
]
}
}
},
{
"name": "overlay",
"type": "web_capture",
"config": {
"url": "https://your-app.example.com/overlay/captions?room=voice-room",
"captureElement": "canvas#stage",
"fps": 30,
"captureAudio": false,
"waitForSelector": {
"selector": "canvas#stage[data-ready=\"true\"]",
"timeout": 20000
}
}
},
{
"name": "carrier",
"type": "video_generator",
"config": {
"width": 16,
"height": 16,
"fps": 5,
"backgroundColor": "#000000"
}
}
],
"nodes": [
{
"name": "captions",
"type": "asr",
"inputs": [
"room"
],
"config": {
"language": "multi"
}
},
{
"name": "floor",
"type": "audio_mixer",
"inputs": [
"room"
],
"config": {}
}
],
"sinks": [
{
"name": "to_room",
"type": "livekit",
"inputs": [
"carrier",
{
"name": "captions",
"select": {
"mediaTypes": [
"data"
]
}
}
],
"config": {
"serverUrl": "wss://your-project.livekit.cloud",
"token": "<publish-token>",
"videoTrackName": "avflow-caption-carrier"
}
},
{
"name": "live",
"type": "rtmp_push",
"inputs": [
{
"name": "overlay",
"select": {
"mediaTypes": [
"video"
]
}
},
"floor"
],
"config": {
"urls": [
"rtmp://ingest.example.com/live/<stream-key>"
],
"encoding": {
"videoCodec": "h264",
"audioCodec": "aac",
"videoBitrateBps": 3500000,
"audioBitrateBps": 128000,
"keyframeIntervalSec": 2
}
}
}
],
"policies": {
"maxDurationSec": 14400,
"idleTimeoutSec": 120
}
} curl -X POST "https://api.avflow.dev/v1/jobs" \
-H "Authorization: Bearer $AVFLOW_API_KEY" \
-H "Content-Type: application/json" \
-d @22-voice-room-captions.json Know the limits before you ship
- You write the overlay page; AVFlow captures it rather than designing it for you.
- The ASR node emits text events — summarization and display logic stay in your app.
- A caption-capable streaming sink still needs a video input to carry subtitles.
Keep reading
Run this LiveKit pipeline
Generate the Job in Playground, then run it with $20 promotional credit.