Nemotron ASR Streaming (Realtime)
Words appearing while it's still listening
- audio
- transcription
This is the node that explains why the model is called what it's called. Nemotron ASR's headline feature isn't accuracy, it's that it recognises speech incrementally - it emits words as audio arrives instead of waiting for the end of the utterance. This node is the thinnest way to see that from inside ComfyUI: text accumulates on the node while the audio is still being pushed.
Caveat first, because it decides whether you want this node at all: if all you have is a finished file and you just want the transcript, use Nemotron ASR Transcribe instead. It's simpler and it doesn't care about WebSockets. Streaming earns its keep when the audio is arriving over time - a live microphone, a growing buffer, a pipeline where you want to see progress instead of watching a spinner.
How it works
The plain Transcribe node does a one-shot multipart POST. This one opens a WebSocket instead, at /v1/audio/transcriptions/realtime, and speaks the server's little event protocol:
- server sends
session.createdon connect - the node sends one
session.updatewithsample_rate,language,automatic_punctuation(on) andword_timestamps(off) - audio goes out as raw binary little-endian PCM16 frames - no WAV container, just samples
input_audio_buffer.commitmarks the end of the utterance- the server streams
conversation.item.input_audio_transcription.deltaevents (the partials you watch appear) and finishes with...completed, which is your final text
chunk_ms is how much audio each frame carries: at 16 kHz, 100 ms is 3,200 bytes. So use_model_rate on means the clip is resampled to the model's 16 kHz input rate before streaming, and chunk_ms of 20 to 1000 sets how finely it's sliced.
The transport is aiohttp, which the ComfyUI server already ships - so, again, nothing new to pip install.
One honest note about mechanism, because it's easy to assume the wrong thing. This node pushes a file as fast as it can - a 2 ms yield between chunks, then commit, then it waits. It is not playing your clip in real time, and it isn't faster than the batch node either. What you get is the model's incremental path and a live progress display. On a pre-recorded file that's a nice-to-have. On a stream, it's the whole point.
Inputs
audio- the wire, same as the other nodes.language- defaults toEnglish (en-US).(auto-detect)can hand you a leading artifact likeKal-on short clips, and this node's default exists to spare you that.chunk_ms- 100 by default. Leave it unless you're chasing latency.show_live_preview- on by default. This is the setting that draws the partial text on the node while the session runs; turn it off if you only care about the final string.use_model_rate- on, and it should be. Resamples to 16 kHz, which is what the model wants. Turn it off only if you have a reason to stream at the native rate.server_url- the base HTTP URL. It gets converted tows://(orwss://fromhttps://) internally.
There is no model input on this node, and that's worth knowing: it always auto-detects from /v1/models. If your server has more than one transcription model loaded, you can't pin which one streaming uses. Batch mode can; streaming can't.
Output and installation
transcription - the final committed transcript, as a STRING, plus the inline preview. Wire it anywhere text goes: CLIP Text Encode, a save-text node, Preview Text.
Install is the same as the rest of the pack. Manager → search comfyui_nemotron_asr → restart, or:
cd ComfyUI/custom_nodes
git clone https://github.com/Rakeshcool/comfyui_nemotron_asr
And start NeMo-Speech.cpp yourself first - these nodes never start it.
When it fails
Cannot open realtime WebSocket to ... means the server is up enough to answer health checks but isn't serving the realtime endpoint - an older build, or a server started without the streaming model. Batch mode may still work, which is why this is confusing if you only test the plain node.
Timed out waiting for 'session.updated' - the connection opened but the server never acknowledged the session config. Check that the WebSocket URL didn't get eaten by a proxy.
No final transcript within ...s - the utterance committed and the server never sent completed. The error message includes the last partials it saw, so read them: full text but no final event is a server-side hiccup, empty partials means the audio never arrived in a form it liked. Check use_model_rate and the sample rate before you start rewriting the workflow.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| audio | AUDIO | AUDIO to transcribe (from LoadAudio, RecordAudio, etc.). | |
| server_url | STRING | http://127.0.0.1:8080 | Base URL of the NeMo-Speech.cpp server. |
| language | COMBO | English (en-US) | Languages supported by Nemotron ASR. '(auto-detect)' lets the server decide. |
| chunk_ms | INT | 10020–1000 | Size of the PCM chunks streamed to the server, in milliseconds. |
| show_live_preview | BOOLEAN | true | Show the partial transcription live on the node while streaming. |
| use_model_rate | BOOLEAN | true | Resample to 16 kHz (the model's input rate) before streaming. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| transcription | STRING | — |