返回首页
Lanson Flow 文档库

How to consume realtime transcription events and build a caption UI that doesn't jitter.

The problem with naive approaches

A naive live caption UI might try to update text on every event. With partial-text systems, this causes visible jitter as words are repeatedly replaced.

LansonAI's StableStream eliminates this: each conversation.item.input_audio_transcription.completed event contains final, stable text.

Recommended render strategy

Append-only display

const captions = document.getElementById('captions');

ws.onmessage = (event) => { const data = JSON.parse(event.data);

if (data.type === 'conversation.item.input_audio_transcription.completed') { // Append directly — text is final, no replacement needed const p = document.createElement('p'); p.dataset.utterance = data.utterance_index; p.textContent = data.text; captions.appendChild(p); captions.scrollTop = captions.scrollHeight; } };

With optional status indicators

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);

if (data.type === 'input_audio_buffer.speech_started') { showIndicator(data.utterance_index, 'listening'); }

if (data.type === 'input_audio_buffer.speech_stopped') { showIndicator(data.utterance_index, 'processing'); }

if (data.type === 'conversation.item.input_audio_transcription.completed') { hideIndicator(data.utterance_index); appendCaption(data.utterance_index, data.text); } };

What you do NOT need to do

  • ❌ No partial → final text replacement mapping
  • ❌ No waiting for "stabilization" (received text is already stable)
  • ❌ No text rollback or undo
  • ❌ No debounce or jitter elimination
  • ❌ No re-rendering of previously shown text
  • Scrolling behavior

    For long sessions, manage scroll behavior:

    function appendCaption(index, text) {
      const p = document.createElement('p');
      p.textContent = text;
    

    // Auto-scroll if user is near bottom const isNearBottom = captions.scrollHeight - captions.scrollTop - captions.clientHeight < 100;

    captions.appendChild(p);

    if (isNearBottom) { captions.scrollTop = captions.scrollHeight; } }

    Styling for readability

    #captions p {
      margin: 0.25em 0;
      padding: 0.25em 0.5em;
      border-radius: 4px;
      transition: opacity 0.2s;
    }
    

    Related

  • [StableStream](/docs/concepts/stablestream) — the stability contract
  • [Stable vs. Partial Text](/docs/concepts/stable-vs-partial-text) — concept
  • [Browser Live Captions](/docs/guides) — full browser setup