Back to the experiment

How this model actually behaves

Everything here was measured against the live API, not copied from the documentation. Where the two disagree, this page describes what the API did.

gpt-live-transcribe · webrtc · verified aug 2026

The life of a piece of text

Why transcript text starts grey, when it turns solid, and why it ends up split into separate sections.

youOpenAIthis appgrey = streamingsolid = finalised
  1. 0 ms
    (you start speaking)

    Audio flows continuously over the peer connection. No client event is needed to begin — adding the microphone track is enough.

    (transcript empty)
  2. ~200 ms
    conversation.item.input_audio_transcription.delta

    The first delta arrives and opens a new item_id. Text is provisional, so it renders grey and italic.

    Hello

    item_E8Sm…3b · streaming

  3. ~400 ms
    …delta

    Deltas keep landing roughly every 200 ms, each appending to the same item.

    Hello, can you

    item_E8Sm…3b · streaming

  4. ~1.2 s
    …delta ×N

    Still one item, still grey. Nothing has been finalised yet — and nothing will be until a commit is sent.

    Hello, can you hear me clearly

    item_E8Sm…3b · streaming

  5. +1.5 s of silence
    → input_audio_buffer.commit

    This app decides the utterance is over and commits. The API never does this on its own for this model.

    Hello, can you hear me clearly

    item_E8Sm…3b · streaming

  6. +~10 ms
    input_audio_buffer.committedconversation.item.addedconversation.item.done

    Bookkeeping acknowledging the commit. The transcript still shows grey text — these three carry no transcript.

    Hello, can you hear me clearly

    item_E8Sm…3b · streaming

  7. +300–700 ms
    conversation.item.input_audio_transcription.completed

    The final transcript replaces the accumulated grey text. Punctuation and casing are often corrected here. This item is now a finished section.

    Hello, can you hear me clearly?

    item_E8Sm…3b · finalised

  8. next audio
    …delta (new item_id)

    Speaking again opens a fresh item below the finished one. That is why the transcript is a stack of sections rather than one paragraph.

    Hello, can you hear me clearly?

    item_E8Sm…3b · finalised

    This is the next

    item_E8Sn…7c · streaming

The 1.5-second rule is ours, not the API's

This model has no turn detection. It will happily stream deltas into a single item forever — it never decides that a sentence ended. Somebody has to draw the boundary, and since the API will not, this app does: after 1.5 seconds without a new delta, it sends input_audio_buffer.commit, which is what produces the final transcript.

The consequence you will actually notice

If you pause for more than 1.5 seconds in the middle of a sentence, that sentence is cut into two sections. Nothing is lost, but the split is real and it happened on the client. If you find yourself thinking “why did it break my sentence there”, this is why.

The trade-off is symmetrical: a shorter timeout finalises text sooner but fragments natural speech more; a longer one keeps sentences whole but leaves text provisional for longer. 1.5 s is a starting point, not a discovered optimum. It lives in IDLE_COMMIT_MS in use-live-transcribe.ts.

Events that arrive

Observed on the oai-events data channel during a normal run.

EventWhenMeaning
session.createdOnce, ~170 ms after the data channel opensSession is live and ready for audio.
conversation.item.input_audio_transcription.deltaEvery ~200 ms while you speakIncremental text for the open item. Appends to whatever came before on the same item_id.
input_audio_buffer.committedImmediately after we send a commitAcknowledges the commit. Carries no transcript.
conversation.item.addedImmediately after a commitThe item is now part of the conversation. No transcript.
conversation.item.doneImmediately after a commitThe item is closed. Still no transcript.
conversation.item.input_audio_transcription.completed300–700 ms after a commitThe final, corrected transcript for that item. This is the only event that finalises text.

Events that never arrive

Waiting on any of these will hang forever.

EventWhy not
input_audio_buffer.speech_startedRequires turn detection, which this model rejects outright.
input_audio_buffer.speech_stoppedSame — no server-side VAD exists for this model.
conversation.item.input_audio_transcription.completed (unprompted)Never arrives on its own. Verified: after audio stopped, five seconds passed with zero completions until a commit was sent.

Because there is no speech_started, the “Transcribing” indicator and the speech-onset timestamp are both derived locally — the former from recent delta arrivals, the latter from the microphone level crossing a calibrated silence floor.

Numbers to expect

From a real run on a Logitech C925e, en+ro, delay: low, noise_reduction: near_field.

StageTypicalNote
Ephemeral token mint~400 msRound trip to our server, which calls OpenAI.
Microphone grant50–450 msNear-instant once permission is remembered.
SDP exchange~500 ms–1 sPOST /v1/realtime/calls.
ICE + DTLS~600 msUntil pc.connectionState is connected.
Total setup1.6–2.0 sstart() through session.created.
Delta cadence~200 msSteady while speech continues.
Commit → completed300–700 msObserved 603, ~500 and ~320 ms across three utterances.

One caveat on benchmarking: the headline “time to first word” is measured from speech onset, not from start(). Measured from start() it would mostly record how long you spent looking at the screen before talking — seconds of variance that bury the few hundred milliseconds separating the delay settings.

US vs EU endpoints

Latency figures preliminary (n=6 per endpoint). EU-does-not-transcribe: confirmed with real speech.

eu.api.openai.com accepts the entire chain on a non-approved account — mint 200, SDP 201, data channel open, session.created, RTP flowing — and never produces a single delta. Confirmed with real speech on a physical microphone, not just the synthetic test signal. The documented enterprise gating evidently applies at inference rather than at authentication: instead of a 403 at the door you get a flawless handshake and eternal silence — the silent-failure archetype this project keeps meeting. The irony: the EU media path is closer (73–81 ms RTT vs 170–183 ms on US), and unusable.

Measurementapi.openai.comeu.api.openai.com
Resolved IPs172.66.0.243, 162.159.140.245identical — same Cloudflare anycast
TLS handshake~66 ms~62–74 ms
Mint, median of 6265 ms (250–419)284 ms (203–1859)

The medians are effectively equal. EU showed a much fatter tail, but one 1.86 s outlier in six probes is not a finding. Both hostnames hit the same edge, so any real difference lives in routing from that edge to the origin — which TLS timings cannot see. That is precisely why the selector exists: accumulate a few dozen runs in the history and read those medians instead of trusting six probes.

The more informative number is the ICE round-trip reported by the connection test. HTTPS terminates at a nearby CDN edge, but RTP goes to real media servers — so the media path can be far away even when the API edge answers in 66 ms.

What the model will not give you

Hard constraints. No configuration unlocks these.

ModelPriceUse
gpt-live-transcribein use$0.017 / minStreaming deltas from live audio. What this experiment uses.
whisper-1$0.006 / minBatch transcription of completed files.
gpt-transcribe$0.0045 / minAsync transcription; in realtime only after a committed turn.

Live streaming costs roughly three times batch transcription. If text is not needed until the recording ends, the cheaper models are the right call.

Troubleshooting

Ordered by how much time each one has already cost.

Connected, but no text ever appears

Check the Outbound audio panel first. If packetsSent is 0, no audio is leaving the browser at all. If packets are flowing but audioLevel is ~0, the microphone is capturing silence — wrong input device, muted at the OS level, or a hardware mute switch. This is the failure mode that looks exactly like a broken API, and it is not one: the session config has been verified working end to end.

Text appears but accuracy is poor

Look at the input level. A webcam microphone measured audioLevel 0.0027 — audible, but very quiet. At that level whole phrases came back mangled and “OK” was transcribed as a caress. Move closer, raise the input gain in the OS, or switch to a headset. Also try far_field noise reduction for a laptop or room microphone, and raise delay to give the model more context per chunk.

Text stays grey and never finalises

A commit was never sent. That only happens if the data channel closed, or if deltas are still arriving so the 1.5 s idle timer keeps resetting. Pressing Stop always forces a final commit.

A sentence got split in two

Expected — you paused for more than 1.5 s. See above.

Yellow “unexpected” tags in the event log

Those mark event types never observed before. The routine commit trio — input_audio_buffer.committed, conversation.item.added, conversation.item.done — is known and is no longer flagged. Anything still highlighted is genuinely new and worth reading.