Nodes/KIE.ai Nodes Next/Gemini 2.5 Pro Text to Speech
ComfyUI Node

Gemini 2.5 Pro Text to Speech

Two characters arguing, straight out of a JSON field

By felipederosilva·Created 2 months ago·Updated 5 days ago· 3
Gemini 2.5 Pro Text to Speech
    • audio
    • url
    • all_urls_json
    • task_id
    • raw_json
    • credits_consumed
    • credits_left
    ◄speakers[ { "speaker_id": "Speaker 1", "voice_name": "Fenrir", "audio_profile": "A stern and weary gatekeeper", "accent": "British (RP)", "style": "Deadpan", "pace": "Natural" }, { "speaker_id": "Speaker 2", "voice_name": "Puck", "audio_profile": "A determined and courageous traveler seeking answers.", "accent": "American (Gen)", "style": "Empathetic", "pace": "Staccato" } ]►
    ◄dialogue_turns[ { "speaker_id": "Speaker 1", "text": "[shouting] Halt, traveler! The northern pass is sealed by order of the council." }, { "speaker_id": "Speaker 2", "text": "[determination] I carry a message for the elder. Step aside, or I will force my way through." }, { "speaker_id": "Speaker 1", "text": "[caution] No one passes. [pensive] The elder is... he's no longer receiving visitors." }, { "speaker_id": "Speaker 2", "text": "It's too late. [whispers] The shadow... it reached him first. [urgency] You need to leave. [shouting] Now." } ]►
    ◄temperature1.00►
    ◄sceneA dark, crumbling dungeon...►
    ◄sample_contextFantasy RPG style...►
    ◄timeout_seconds1200►
    ◄callback_url►

    Most text-to-speech in ComfyUI is one voice reading one block of text. This node is the other thing: a multi-speaker dialogue generator. You define a cast - each with a voice name, an accent, a speaking style, a pace, a one-line character brief - then hand it a script as a list of turns, and it returns a single AUDIO object with the whole scene performed in order.

    That's genuinely useful for the thing people keep trying to do with video models: a two-hander scene with dialogue, where you need the acting to be reacting rather than two monologues stitched together. It also pairs naturally with the video side of this hobby - generate the audio, drop the file into an audio-driven video node, and you have a talking scene without a microphone.

    How the node works

    It's a KIE Market job: the node submits {"speakers": [...], "dialogue_turns": [...], ...} with the model google/gemini-2-5-pro-tts, polls the task until it completes, downloads the audio, and returns it as a native ComfyUI AUDIO. speakers, dialogue_turns and their JSON structure are text widgets, not structured inputs - so everything you know about JSON validity applies, and the pack's local preflight will reject malformed JSON before it spends anything.

    The two big fields are the whole interface:

    • speakers - a JSON list of cast entries. Each has a speaker_id that must be in the Speaker N format, a voice_name from Google's 30-voice list (Fenrir, Puck, Zephyr, Kore, Aoede, and so on), plus descriptive strings for audio_profile, accent, style and pace. The descriptions are the interesting part - they're direction, and the model uses them.
    • dialogue_turns - a JSON list of {"speaker_id": ..., "text": ...} in the order they should be spoken. Square-bracket cues like [shouting] or [whispers] inside the text act as per-line direction. That's the trick that makes it sound like acting instead of narration.

    Optional: scene (where the conversation happens), sample_context (overall tone - "audiobook narration, gentle and inviting" style framing), temperature (default 1, 0–2, sampling warmth), timeout_seconds, and callback_url which you should leave empty.

    Outputs: audio, url, all_urls_json, task_id, raw_json, credits_consumed, credits_left.

    Wiring the output

    audio is a normal ComfyUI AUDIO, so SaveAudio works, as do lip-sync and audio-driven video nodes. If your clip is going into a video workflow, remember the source-video audio gets replaced when you mix - connect dialogue explicitly rather than assuming it survives. And this node isn't a terminal node: if audio goes nowhere, the node never runs and never bills you.

    Installing

    cd ComfyUI/custom_nodes
    git clone https://github.com/felipederosilva/ComfyUI-KIE-Nodes-Next
    cd ComfyUI-KIE-Nodes-Next && pip install -r requirements.txt
    # restart ComfyUI
    

    Manager: search "KIE.ai Nodes Next". The whole pack's dependency list is requests, Pillow, numpy, PyYAML - no TTS model to download, no GPU voice cloning, nothing heavy. Set the key once at Settings → KIE.ai Nodes Next → Connection; it's validated before it replaces an existing key, stored outside workflow JSON, and there's no environment-variable option, by design.

    Where it goes wrong

    Editing those JSON blobs by hand is where 90% of the pain lives. A trailing comma, a speaker_id of "Narrator" instead of "Speaker 1", a voice_name that isn't on the list - the preflight catches the syntax, but the provider catches semantic mismatches, and a turn referencing a speaker that doesn't exist will fail there rather than locally.

    Also worth internalising: the output is one continuous file. There's no per-turn export, so if you need an individual line isolated for a video take, you get to cut it yourself.

    And the taste check - this is cloud TTS with per-character billing, and the local alternative is real: the open TTS stack in ComfyUI is now good enough for narration, and free per call. Where this node wins is the multi-speaker performance with direction, which the local options still don't do cleanly. Use it for scenes, not for reading a paragraph.

    CategoryKIE Next/Audio/Google/Gemini TTS

    Inputs (7)

    NameTypeDefaultDescription
    speakersSTRING[ { "speaker_id": "Speaker 1", "voice_name": "Fenrir", "audio_profile": "A stern and weary gatekeeper", "accent": "British (RP)", "style": "Deadpan", "pace": "Natural" }, { "speaker_id": "Speaker 2", "voice_name": "Puck", "audio_profile": "A determined and courageous traveler seeking answers.", "accent": "American (Gen)", "style": "Empathetic", "pace": "Staccato" } ]List of speaker configurations
    dialogue_turnsSTRING[ { "speaker_id": "Speaker 1", "text": "[shouting] Halt, traveler! The northern pass is sealed by order of the council." }, { "speaker_id": "Speaker 2", "text": "[determination] I carry a message for the elder. Step aside, or I will force my way through." }, { "speaker_id": "Speaker 1", "text": "[caution] No one passes. [pensive] The elder is... he's no longer receiving visitors." }, { "speaker_id": "Speaker 2", "text": "It's too late. [whispers] The shadow... it reached him first. [urgency] You need to leave. [shouting] Now." } ]List of dialogue turns, output in sequential order
    temperatureoptFLOAT1.000–2Sampling temperature, e.g., 1
    sceneoptSTRINGA dark, crumbling dungeon...Scene description, e.g., "A quiet, warm room with a fireplace crackling softly."
    sample_contextoptSTRINGFantasy RPG style...Sample context/overall tone, e.g., "Audiobook style narration. Tone is gentle and inviting."
    timeout_secondsoptINT120030–7200—
    callback_urloptSTRINGOptional KIE callback URL. Leave empty for normal ComfyUI use.

    Outputs (7)

    NameTypeDescription
    audioAUDIO—
    urlSTRING—
    all_urls_jsonSTRING—
    task_idSTRING—
    raw_jsonSTRING—
    credits_consumedFLOAT—
    credits_leftFLOAT—