Chatterbox SRT / Subtitles π
The subtitle file is the script, and the node rewrites it to match the audio
- audio_prompt
- audio
- generation_info
- timing_report
- Adjusted_SRT
Every TTS tool will read your text. Almost none of them will read your timings, and that's the whole point of a subtitles node. You paste an SRT, the model speaks each cue as its own clip, and - here's the interesting part - the node then tells you the truth about what it made: a new SRT whose timestamps match the audio you actually got, plus a report on what it had to bend to get there.
Why this exists
SRT-driven TTS in ComfyUI has a lineage. The big multi-engine TTS Audio Suite everyone uses was originally a Chatterbox SRT tool before it grew into a hub wrapping F5, VibeVoice, Qwen3-TTS and friends. The pitch is the same here: an SRT is already a shot list for narration. If you have a video cut to length and a subtitle file to match, the file is your script - you don't want to re-time your edit because the voice took nine seconds where you wrote seven.
And that's the actual problem this node solves, because a cue synthesised on its own is however long the model said it was. Written slot: three seconds. Real output: five. Two cues like that and your narration has drifted half a minute behind the picture.
The four timing modes
timing_mode is the setting you're really choosing, and the other timings widgets are just its limits.
smart_natural(default) - keeps the audio natural, shifts later cues when one runs long, and applies a bounded time-stretch only when drift exceedstiming_tolerance. It never knowingly emits overlapping cues; if honouring the original timings would force an overlap, the track gets re-laid out and the fallback is recorded intiming_report.stretch_to_fit- obeys the file exactly and time-stretches audio to each written slot. Distorts the voice. Sometimes that's the trade you want.pad_with_silence- natural audio at the SRT start times, gaps filled with silence. Can overlap, and the tooltip says so.concatenate- ignores the written timings completely, speaks cues back to back, and writes a fresh SRT from the real timings. The right mode when the SRT was only ever a script and never a schedule.
The stretch limits are worth understanding because the defaults are opinionated. max_stretch_ratio defaults to 1.0, meaning no expansion at all: out of the box only compression is enabled, on the reasoning that slowing speech down is the adjustment a listener always notices. min_stretch_ratio of 0.5 is twice as fast - about as far as synthesis stays intelligible. timing_tolerance at 2.0 lets a line run to three times its written length untouched. And fade_for_StretchToFit (0.01) applies a head/tail fade to stretched clips, because a resampled join is a step discontinuity and a step is a click.
Inputs and outputs
Required: model_pack_name, srt_content (standard SRT, HH:MM:SS,mmm), timing_mode, and then the synthesis knobs - max_new_tokens (25 tokens is roughly one second of audio, so a long cue needs more than the default 1000), flow_cfg_scale, exaggeration, temperature, seed, use_watermark, plus timing_tolerance, max_stretch_ratio, min_stretch_ratio, fade_for_StretchToFit, the runtime device / dtype / attention_mode trio, and crash_protection_template for cues under 21 characters. Subtitle cues are short by nature - "Yes." is a cue - and that's the shape of input upstream Chatterbox fails on.
Optional audio_prompt is the narrator's reference clip; unconnected, you get the checkpoint's built-in voice.
Four outputs, and all four are useful:
audio- the spoken track, already laid out totiming_mode.Adjusted_SRT- the corrected subtitle file. This strings into a text-save node or your own.srtwrite, and it's the file you ship next to the video.timing_report- what the reconciliation actually did, including any fallback re-layouts.generation_info- per-cue generation stats.
Keep temperature low here. It's not just taste: a cue whose length changes run to run changes the subtitle file too, so the same prompt gives you a different SRT on every queue.
Install
Manager β Install Custom Nodes β search ComfyUI-Chatterbox β Install β restart. Manual:
cd ComfyUI/custom_nodes
git clone https://github.com/wildminder/ComfyUI-Chatterbox.git
pip install -r ComfyUI-Chatterbox/requirements.txt
One added dependency, s3tokenizer; the pack vendored Chatterbox's DSP in pure numpy/torch and dropped librosa, scipy, omegaconf and conformer. ComfyUI >=0.37.0 - it's a V3-API pack, so no NODE_CLASS_MAPPINGS inside, and that's expected. Python >=3.10. Model packs download on first run into ComfyUI/models/tts/chatterbox/, ~2.5 GB each.
Where people get burned
Truncated cues. A cue that reads long hits the token budget and gets cut off. max_new_tokens is per cue, so raise it rather than editing your script.
Fighting the defaults. If you want cues stretched to the file, that's stretch_to_fit, and you should expect the voice to sound stretched. smart_natural is the default because it's the mode that keeps speech sounding like speech while still refusing overlaps.
Install troubles on old interpreters. s3tokenizer is the one dependency that has caused real install failures - reported on Python 3.10 setups (Stability Matrix uses 3.10) while 3.12 is fine. If pip fails, that's the first suspect, not the pack.
Upgrading from 1.x. Version 2.0.0 moved the model packs from models/chatterbox_tts/ to models/tts/chatterbox/ with no compatibility shim. Move the folders or delete the old directory by hand.
Inputs (18)
| Name | Type | Default | Description |
|---|---|---|---|
| model_pack_name | COMBO | resembleai_default_voice | Select the Chatterbox checkpoint directory to load. |
| srt_content | STRING | 1 00:00:00,000 --> 00:00:03,000 The first line of the subtitle file. 2 00:00:03,000 --> 00:00:06,500 The second line, three and a half seconds long. 3 00:00:06,500 --> 00:00:09,000 And a third to finish with. | The subtitle file to speak. Standard SRT, with 'HH:MM:SS,mmm' timestamps. Each cue is synthesised separately, so a cue's length is whatever the model said, not what the file promised -- which is the problem timing_mode exists to solve. Character tags ([Alice]), language tags ([de:Alice]), pause tags ([pause:1.5]) and the v2 angle-bracket tokens all work inside a cue, exactly as they do on the main TTS node. |
| timing_mode | COMBO | smart_natural | How the model's timings are reconciled with the file's. stretch_to_fit - stretches/compresses the audio to exactly match each SRT segment's duration. Obeys the file; distorts the voice. pad_with_silence - natural audio at the SRT start times, gaps padded with silence. May result in overlaps. smart_natural (default) - keeps the audio natural, shifts later segments when a line runs long, and applies a bounded stretch only when the drift is bigger than timing_tolerance. Never emits overlapping cues: if honouring the timings would overlap, the track is re-laid out and the fallback is recorded in timing_report. concatenate - ignores the original timings entirely, speaks the cues back to back, and writes a new SRT from the actual timings. |
| max_new_tokens | INT | 10001β5000 | Token budget per cue. 25 tokens is roughly one second, so a cue written long needs more than the default. |
| flow_cfg_scale | FLOAT | 0.700β1 | Flow-matching CFG scale for the mel decoder. |
| exaggeration | FLOAT | 0.500.25β2 | Controls the expressiveness and emotional intensity. |
| temperature | FLOAT | 0.800.05β5 | Controls the randomness of the sampling process. Lower values are more deterministic, which matters here: a cue whose length changes run to run changes the subtitle file too. |
| seed | INT | 00β18446744073709550000 | Seed for random number generation. A value of 0 will use a random seed. |
| use_watermark | BOOLEAN | false | Enable or disable the audio watermark. Requires 'resemble-perth' to be installed. |
| timing_tolerance | FLOAT | 2.00.5β10 | How far a cue's realised length may miss its SRT slot before a bounded stretch is applied, as a multiple of that slot. 2.0 lets a line run to three times its written length untouched; lower it toward 0.5 to keep the subtitles tight. |
| max_stretch_ratio | FLOAT | 1.00.5β5 | Upper bound on the time-stretch factor. The default of 1.0 never makes a cue longer -- only compression is enabled out of the box, because slowing speech down is the adjustment a listener always notices. Raise it above 1.0 to allow expansion. |
| min_stretch_ratio | FLOAT | 0.50.1β2 | Lower bound on the time-stretch factor. 0.5 is twice as fast as the original, about as fast as a synthesised voice stays intelligible. |
| fade_for_StretchToFit | FLOAT | 0.0100β0.1 | Head/tail fade applied to a stretched clip, as a fraction of its length. A resampled join is a step discontinuity, and a step is a click. |
| device | COMBO | default | Device to run inference on. 'auto' follows ComfyUI's default compute device. |
| dtype | COMBO | auto | Data type for model precision. 'auto' lets ComfyUI pick the best type for the target device. |
| attention_mode | COMBO | sdpa | Attention implementation. Backends this machine cannot run are left out of the dropdown; a saved graph that names one anyway falls back to 'sdpa'. |
| crash_protection_template | STRING | hmm ,, {seg} hmm ,, | Padding applied to any cue shorter than 21 characters. Subtitle cues are short by nature -- 'Yes.' is a cue -- and upstream Chatterbox fails on short text in sequential generation. {seg} is replaced by the cue's text. Set to '' to disable. |
| audio_promptopt | AUDIO | Reference audio to clone the narrator's voice from. When left unconnected the checkpoint's built-in voice is used. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| audio | AUDIO | β |
| generation_info | STRING | β |
| timing_report | STRING | β |
| Adjusted_SRT | STRING | β |