ComfyUI Extension: APZmedia Qwen TTS Nodes
Run ComfyUI workflows without the setup
No installs, no CUDA version roulette, no GPU sitting idle on your bill. Bring a workflow and run it in the browser.
ComfyUI nodes for Qwen3-TTS: voice design, voice library, segmented generation
Looking for a different extension?
Custom Nodes (10)
README
APZmedia Qwen TTS — ComfyUI Node Suite
ComfyUI custom nodes for Qwen3-TTS: voice design, reusable voice library, voice cloning, per-segment emotion control, and flexible audio export. English UI throughout.
Requirements
- ComfyUI with Python 3.10+ (3.12 recommended)
- PyTorch 2.4+ — already in your ComfyUI venv; do not pip-install a second torch
- GPU strongly recommended; CPU works with fp32 but is slow
- Models download automatically from Hugging Face on first use
Install
cd /path/to/ComfyUI/custom_nodes
git clone https://github.com/APZmedia/ComfyUI-APZmedia-qwen-TTS
cd comfyui-apzmedia-qwen-tts
pip install -r requirements.txt
Or install via ComfyUI Manager once the package is published.
Models
Three Qwen3-TTS model families — load each with APZmedia: Model Loader:
| Model ID | Size | Use for |
|----------|------|---------|
| Qwen/Qwen3-TTS-12Hz-1.7B-VoiceDesign | 1.7B | Voice Design, Voice Design Generate, Segmented Generate |
| Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice | 1.7B | Custom Voice Generate (preset speakers) |
| Qwen/Qwen3-TTS-12Hz-1.7B-Base | 1.7B | Voice Clone Generate, building embeddings in Voice Design |
| Qwen/Qwen3-TTS-12Hz-0.6B-CustomVoice | 0.6B | Lightweight preset-speaker generation |
| Qwen/Qwen3-TTS-12Hz-0.6B-Base | 0.6B | Lightweight cloning |
Nodes
APZmedia: Model Loader
Loads a Qwen3-TTS model. Outputs a QWEN_TTS_MODEL handle used by all generation nodes.
| Widget | Description |
|--------|-------------|
| model_name | Hugging Face model ID (dropdown) |
| precision | bf16 (default, GPU), fp16, or fp32 (CPU fallback) |
| device | auto, cuda, or cpu |
APZmedia: Voice Design
Creates a reusable voice identity from a natural-language description.
Requires two models: the VoiceDesign model generates a reference audio clip from your description; the Base model converts that clip into a speaker embedding (VoiceClonePromptItem) that can be reused across many generation calls.
This is a one-time setup step. After saving to the library you only need the Base model for generation.
| Input | Description |
|-------|-------------|
| model_design | VoiceDesign model handle |
| model_base | Base model handle |
| voice_description | Natural-language voice + style, e.g. "Deep authoritative male, slightly amused" |
| reference_text | Short transcript spoken in the reference clip (≤ 20 words; must match audio exactly) |
| language | Language of the reference text |
| x_vector_only_mode | False (default): full in-context learning — best fidelity, needs accurate ref_text. True: speaker x-vector only — faster, ref_text not needed |
| seed | Reproducibility seed |
| Output | Description |
|--------|-------------|
| voice_prompt | Speaker embedding — wire into Voice Clone Generate or Voice Library Save |
| reference_audio | The generated reference clip — wire into Voice Library Save or preview |
| reference_text | Passthrough — wire into Voice Library Save |
| voice_description | Passthrough — wire into Voice Library Save as description |
Typical wiring:
[Model Loader: VoiceDesign] [Model Loader: Base]
↓ ↓
model_design model_base
↘ ↙
[Voice Design]
↙ ↓ ↓ ↘
voice_prompt audio ref_text voice_desc
↓ ↓ ↓ ↓
[Voice Library Save]
APZmedia: Voice Design Generate
Generate speech with a natural-language voice direction. Single call, no voice identity saved. Uses the VoiceDesign model only.
| Input | Description |
|-------|-------------|
| model | VoiceDesign model handle |
| text | Text to speak |
| instruct | Voice + style direction, e.g. "Warm female, calm and serious" |
| language | Output language |
| seed | Reproducibility seed |
APZmedia: Custom Voice Generate
Generate speech with preset Qwen3-TTS speakers (Ryan, Vivian, etc.). Uses the CustomVoice model.
| Input | Description |
|-------|-------------|
| model | CustomVoice model handle |
| text | Text to speak |
| speaker_preset | Preset speaker dropdown |
| speaker_name | (optional) Override with a custom speaker name not in the list |
| language | Output language |
| instruct | (optional) Emotion/style direction, e.g. "Very happy", "Speak in a calm and serious tone" |
| seed | Reproducibility seed |
APZmedia: Voice Clone Generate
Generate speech using a saved voice identity. Uses the Base model with a voice_prompt embedding or a raw reference audio + transcript.
| Input | Description |
|-------|-------------|
| model | Base model handle |
| text | Text to speak |
| language | Output language |
| seed | Reproducibility seed |
| voice_prompt | (optional) Speaker embedding from Voice Design or Voice Library Loader |
| ref_audio | (optional) Raw reference audio if no voice_prompt |
| ref_text | (optional) Transcript for ref_audio (required unless x_vector_only_mode) |
| instruct | (optional) Additional style direction |
| x_vector_only_mode | Skip transcript-based ICL; use speaker x-vector only |
Provide either voice_prompt or ref_audio (not both required).
APZmedia: Voice Library Save
Save a voice to a named folder on disk (WAV + metadata JSON).
| Input | Description |
|-------|-------------|
| audio | Reference audio to save |
| name | Subfolder name for this voice |
| library_path | Absolute folder path. Empty → ComfyUI/output/apzmedia_voice_library/ |
| voice_prompt | (optional) Noted in session; embedding is not serialized (WAV is the durable format) |
| ref_text | (optional) Wire from Voice Design's reference_text output |
| description | (optional) Wire from Voice Design's voice_description output |
Library structure on disk:
apzmedia_voice_library/
narrator_warm/
reference.wav
metadata.json ← name, description, created, sample_rate, ref_text
nervous_teen/
reference.wav
metadata.json
APZmedia: Voice Library Loader
Load a saved voice and rebuild its speaker embedding. Requires the Base model.
| Input | Description |
|-------|-------------|
| model_base | Base model handle |
| library_path | Folder containing voice subfolders |
| voice_name | Subfolder name of the voice to load |
| x_vector_only_mode | See Voice Design for explanation |
| Output | Description |
|--------|-------------|
| reference_audio | The saved WAV as ComfyUI AUDIO |
| voice_prompt | Rebuilt speaker embedding — wire into Voice Clone Generate |
Use APZmedia: Voice Library List to see what voices are available before typing a name.
APZmedia: Voice Library List
Browse available voices in a library folder. Useful to discover voice names before using Voice Library Loader.
| Input | Description |
|-------|-------------|
| library_path | Library folder to inspect |
Outputs a formatted STRING listing each voice name, description, and ref_text preview.
APZmedia: Segmented Generate
Generate a multi-segment script where each segment has its own voice direction. Uses inline [voice: ...] markup. VoiceDesign model only.
Markup syntax
[voice: description] text to speak [/voice]
Plain text outside tags uses default_instruct.
Example:
[voice: warm and welcoming] Good morning, and welcome to the show. [/voice]
[voice: tense and hushed] What I'm about to tell you must not leave this room. [/voice]
[voice: cheerful and upbeat] But first — today's sponsor! [/voice]
Segments are generated sequentially and concatenated into one audio output.
| Input | Description |
|-------|-------------|
| model | VoiceDesign model handle |
| script | Full script with [voice: ...] markup |
| default_instruct | Voice direction for text outside tags |
| language | Language for all segments |
| seed | Reproducibility seed |
| Output | Description |
|--------|-------------|
| audio | Concatenated audio of all segments |
| segment_count | Number of segments processed |
APZmedia: Audio Saver
Save audio to disk at any absolute path with a filename template.
| Input | Description |
|-------|-------------|
| audio | ComfyUI AUDIO to save |
| output_path | Absolute folder path. Empty → ComfyUI output folder |
| filename_template | Template string with tokens (see below) |
| sample_rate | Output sample rate (8000–48000 Hz, default 24000) |
| speaker | (optional) Used in {speaker} token |
| text_preview | (optional) Used in {text_preview} token |
| seed | (optional) Used in {seed} token |
Filename tokens:
| Token | Expands to |
|-------|-----------|
| {date} | 20260329 |
| {datetime} | 20260329_143022 |
| {speaker} | Value of the speaker input |
| {text_preview} | First ~40 chars of text_preview, sanitized |
| {seed} | Seed value as string |
Example template: {datetime}_{speaker}_{text_preview}.wav
Complete workflows
Workflow 1 — Quick generation with voice direction
[Model Loader: VoiceDesign] → [Voice Design Generate]
text: "Hello world"
instruct: "Warm, friendly female"
↓
[Audio Saver]
Workflow 2 — Design a voice, save to library, reuse
[Model Loader: VoiceDesign] [Model Loader: Base]
↓ ↓
model_design model_base
↘ ↙
[Voice Design]
↙ ↙ ↓ ↘
voice_prompt audio ref_text voice_desc
↓ ↓ ↓
[Voice Library Save] name: "narrator"
── later, in another workflow ──
[Model Loader: Base] → [Voice Library Loader] voice_name: "narrator"
↓ voice_prompt
[Voice Clone Generate] text: "Any line you want"
↓
[Audio Saver]
Workflow 3 — Multi-character script
[Model Loader: VoiceDesign]
↓
[Segmented Generate]
script:
[voice: gruff old man] I've seen things you wouldn't believe. [/voice]
[voice: curious young woman] Like what? [/voice]
[voice: gruff old man, quieter] Things best forgotten. [/voice]
↓
[Audio Saver]
Audio format
All generation nodes output a ComfyUI AUDIO dict:
waveform:torch.Tensorshaped[1, 1, samples], float32, range[-1, 1]sample_rate: integer (typically 24000 Hz from Qwen3-TTS)
Compatible with ComfyUI's native PreviewAudio node and any node that accepts AUDIO.
Optional: FlashAttention 2
Reduces VRAM usage on CUDA with bf16/fp16. Install per the Qwen3-TTS README. The nodes detect and use it automatically.
License
MIT — see LICENSE.
Run ComfyUI workflows without the setup
No installs, no CUDA version roulette, no GPU sitting idle on your bill. Bring a workflow and run it in the browser.