Breeze TTS 2 Multi-Speaker
A whole radio play from one script box — up to 8 voices, cloned and designed mixed together
- breeze_model
- speaker_1
- speaker_2
- speaker_3
- speaker_4
- speaker_5
- speaker_6
- speaker_7
- speaker_8
- audio
This is the flagship - the node that justifies the whole pack. You write (or paste) a dialogue script, wire in up to 8 Speaker nodes as your cast, and Multi-Speaker generates the entire conversation as one audio file: every turn spoken by the right character, cloned voices and designed voices freely mixed. It's the most "how did this run locally?" thing in the pack, and it's what makes Breeze TTS 2 interesting for anything with characters - podcasts, audiobook dramatizations, NPC chatter, cutscenes.
The author's pack lineage (Zonos2, Higgs v3) all trended toward multi-speaker dialogue, and this is the most polished version of the idea: an LLM-friendly script format, forgiving speaker matching, and validation that fails fast instead of burning GPU time.
How it works
You get two ways to write the script, both accepted in the same text box:
Plain text - one Name: line per line. A line without a name continues the previous speaker; [Name]: and markdown **Name:** also work:
Ada: (sigh) Who steered my ship into the harbor wall this time?
Bob: Technically, captain, the harbor steered into us.
Ada: That is not how harbors work, Bob.
JSON - ideal for LLM-written scripts. If the text starts with [ or {, it's parsed as JSON: a list of {"speaker": "Ada", "text": "..."} objects. Key aliases like name/character and line/content are accepted, and a {"script": [...]} wrapper works too. There's a SKILL.md in the repo you can hand to an LLM to get well-formed scripts out of it.
Before generation starts, the node validates every speaker name against the cast - a typo'd name fails instantly, not after minutes of compute. Speaker matching is forgiving about case, spaces, and punctuation (Ali G matches a speaker named alig). Then it encodes each reference clip once, gives each speaker a stable seed offset (so a designed voice stays consistent across turns), and generates turn by turn - the console shows a summary line and a per-turn progress bar.
The inputs that matter
text- the script. Thetextwidget can be converted to an input (right-click) so you can wire it from a text-generation node.speaker_1…speaker_8- the cast slots; wire Breeze TTS 2 Speaker nodes in. You need at least one.pause_between_speakers- seconds of silence between turns, default 0.3. Crank it for a more deliberate radio-play pace.max_new_tokens- caps total audio frames (12.5 per second of speech); the model stops at EOS on its own, so this is a safety ceiling, and the node errors if your script overflows the 2048-token context.
The rest (temperature, top_k/top_p, repetition_penalty, depth_*, seed) are the shared generation controls - seed matters if you want the same performance back.
Wiring it up
Load Model → Speaker nodes (one per character) → Multi-Speaker → Save Audio. The audio output is a single concatenated 24 kHz mono track with silence padding between turns - ready to drop under a video or into a lip-sync stage.
Installing and troubleshooting
cd ComfyUI/custom_nodes
git clone https://github.com/saganaki22/ComfyUI-Breeze-TTS-2
or ComfyUI Manager → search "ComfyUI-Breeze-TTS-2", restart, let the model download to ComfyUI/models/breezetts2/.
Where people get burned: the script uses a name with no matching Speaker node (fix the cast or the name); a designed speaker has no instruction; duplicate speaker names trip an error; and long reference clips eat the context budget, leaving "no room to speak." The validation catches most of these before generation, which is exactly the point - read the console message and fix the script, not the GPU.
Inputs (20)
| Name | Type | Default | Description |
|---|---|---|---|
| breeze_model | BREEZE_TTS2_MODEL | — | |
| text | STRING | Alice: Hey Bob, did you finish recording the demo? Bob: (sigh) Almost. I just need one more take. Alice: (laugh) That is what you said an hour ago! | Dialogue script: one 'Name: line' per line (a line without a name continues the previous speaker; '[Name]:' also works), or paste a JSON list like [{"speaker": "Alice", "text": "Hi!"}] — handy for LLM-written dialogue. Inline vocal events like (laugh) (sigh) (cough) or [笑] work in both formats. The widget can be converted to an input to wire a string node. |
| pause_between_speakers | FLOAT | 0.300–3 | Seconds of silence between dialogue turns. |
| max_new_tokens | INT | 150064–3000 | Maximum audio frames to generate (12.5 frames per second of speech; the model stops at EOS by itself). |
| temperature | FLOAT | 0.900–2 | Backbone sampling temperature. |
| top_k | INT | 500–1024 | Backbone top-k (0 disables). |
| top_p | FLOAT | 1.000–1 | Backbone top-p (1.0 disables). |
| repetition_penalty | FLOAT | 1.100–2 | HF-style repetition penalty on generated backbone tokens. |
| depth_temperature | FLOAT | 0.900–2 | Depth decoder (codebook 1-15) sampling temperature. |
| depth_top_k | INT | 500–1024 | Depth decoder top-k (0 disables). |
| depth_top_p | FLOAT | 1.000–1 | Depth decoder top-p (1.0 disables). |
| seed | INT | 420–2147483647 | 0 uses the current random state. A positive value is repeatable. |
| speaker_1opt | BREEZE_SPEAKER | Cast slot 1. Wire a Breeze TTS 2 Speaker node here. | |
| speaker_2opt | BREEZE_SPEAKER | Cast slot 2. Wire a Breeze TTS 2 Speaker node here. | |
| speaker_3opt | BREEZE_SPEAKER | Cast slot 3. Wire a Breeze TTS 2 Speaker node here. | |
| speaker_4opt | BREEZE_SPEAKER | Cast slot 4. Wire a Breeze TTS 2 Speaker node here. | |
| speaker_5opt | BREEZE_SPEAKER | Cast slot 5. Wire a Breeze TTS 2 Speaker node here. | |
| speaker_6opt | BREEZE_SPEAKER | Cast slot 6. Wire a Breeze TTS 2 Speaker node here. | |
| speaker_7opt | BREEZE_SPEAKER | Cast slot 7. Wire a Breeze TTS 2 Speaker node here. | |
| speaker_8opt | BREEZE_SPEAKER | Cast slot 8. Wire a Breeze TTS 2 Speaker node here. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| audio | AUDIO | — |