// docs / websocket-obs

WebSocket & OBS

LiveAudio runs a local WebSocket server that broadcasts subtitle JSON in real time. OBS is the built-in target via the included subtitulos_obs.html overlay, but any HTML or WebSocket client on localhost can connect and receive the same broadcast.

Server details

ParameterValue
ProtocolWebSocket (ws://)
Host127.0.0.1 (localhost only)
Port8765 by default, configurable via ws_port
FormatJSON
AuthNone — bound to localhost only

JSON payload schema

subtitle.json
{
  "id": "1760000000000-12",
  "text": "Hello everyone, welcome to the stream",
  "style": "default",
  "created_at": 1760000000.0,
  "processed_at": 1760000001.3,
  "queue_delay": 0.2,
  "total_delay": 1.3,
  "latency": 1.1,
  "is_replay": false,
  "catchup_interval_sec": 0.0
}
FieldTypeDescription
idstringStable identifier for the phrase within the session.
textstringTranscribed text, after blacklist filtering.
stylestringVisual style: default, karaoke, neon, minimal, bold, rgb, typewriter.
created_atnumberTimestamp when the audio segment was created.
processed_atnumberTimestamp when transcription completed.
queue_delaynumberSeconds the audio waited before entering the ASR engine.
total_delaynumberTotal delay from audio capture to a ready subtitle (sub-second to ~1 s on a typical setup).
latencynumberSeconds Whisper spent transcribing the segment.
is_replaybooleanWhether this is a backlog / catch-up subtitle.
catchup_interval_secnumberRecommended pacing between backlog items in auto mode.

The built-in OBS overlay only consumes text and style. The remaining fields are metadata for diagnostics and backlog control — useful if you build your own client, ignorable if you do not.

Subtitle styles

The style field selects one of seven built-in looks, switchable from the Subtitles tab:

StyleLook
defaultWhite text on a semi-transparent black background, soft fade in/out.
karaokeWords appear staggered with a popIn animation; bright yellow with a black outline.
neonCyan glow, uppercase, wide letter-spacing.
minimalNo background, subtle fade — for streams with their own design.
boldHigh-contrast thick text, fast animation, strong on-screen presence.
rgbEach word takes a different rainbow color.
typewriterWords type in one at a time with a blinking cursor; monospaced.

OBS backlog policy

The backlog policy controls only what shows live in OBS. Everything valid is always saved to disk regardless of policy.

ModeConfig valueBehavior
AutoautoSends fresh subtitles plus a short, paced backlog. Drops anything past the max delay.
Live onlylive_onlySaves everything; shows only what is within subtitle_max_live_delay_sec.
Send allsend_allSends everything to OBS even when it arrives late.

Security

  • Localhost only — the server rejects any connection that is not from 127.0.0.1, ::1 or localhost.
  • No authentication required, because it is bound to localhost.
  • Broadcast — multiple clients receive the same messages simultaneously.

OBS Browser Source setup

  • In OBS, Sources → + → Browser.
  • Choose Local file and select subtitulos_obs.html (in liveaudio/assets/). Set size 1920×200.
  • Set FPS to 30 or 60, and place the source bottom-center in your scene.
  • Disable "Shut down source when not visible" and "Refresh browser when scene becomes active" — both can drop the WebSocket connection.
  • For a custom port, set ws_port in config.json; the OBS HTML reads ?port=XXXX from its URL (default 8765).

Connection behavior

  • Reconnect. A client that loses the connection retries about every 3 seconds until the server is back — the bundled overlay does this for you.
  • Broadcast. Every connected client receives every message. Multiple clients (OBS plus a debug browser tab) all see the same stream, and a slow client does not block the others.
  • No backpressure. On websockets ≥ 16 the server uses broadcast(), which fans out without waiting on any single client.

Build your own client

Any localhost WebSocket client can subscribe to the same broadcast OBS reads. A minimal browser client — open the file directly, or load it as an OBS Browser Source:

client.html
<!-- open this file in a browser, or load it as an OBS Browser Source -->
<div id="caption"></div>
<script>
  // ?port=XXXX overrides the default; matches ws_port in config.json
  const port = new URLSearchParams(location.search).get("port") || "8765";
  let ws;
  function connect() {
    ws = new WebSocket("ws://127.0.0.1:" + port);
    ws.onmessage = (e) => {
      const msg = JSON.parse(e.data);       // the subtitle payload
      document.getElementById("caption").textContent = msg.text;
    };
    // the server only broadcasts — reconnect every 3 s if it drops
    ws.onclose = () => setTimeout(connect, 3000);
  }
  connect();
</script>

Or a minimal Python client, to print every payload as it arrives (requires the websockets package):

ws_client.py
import asyncio, websockets

async def test():
    async with websockets.connect("ws://127.0.0.1:8765") as ws:
        async for msg in ws:
            print(msg)

asyncio.run(test())

Debug signals

Open the overlay in a browser and press F12 → Console. These messages tell you where a stalled overlay is stuck:

Console messageMeaning
Conectado al motor ASRConnected successfully — the WebSocket is live.
Desconectado. Reintentando…Cannot connect: LiveAudio is not started, the port is busy, or the ?port= in the URL does not match ws_port.
Error parseando WebSocketA non-JSON / non-dict message reached the overlay — the payload was not a valid subtitle object.