Multi-Speaker Dialogue Audio in a Single Generation
Chaining one TTS call per line produces two monologues stitched together. One call that reads the whole script sounds like an actual conversation.
Two-person scripts get generated the same broken way surprisingly often: split the lines by speaker, run each one through a single-voice text-to-speech call, stitch the results back together in order. It produces the right words in the right order and it never quite sounds like two people talking. What it sounds like is two people who recorded their halves in separate rooms, months apart, and had it edited together after the fact — which, mechanically, is exactly what happened.
Why chaining single-voice calls produces two monologues
Every isolated text-to-speech call starts from nothing. It has no idea what was said in the line before it, no idea what's coming after, no sense of whether it's opening a thought, jumping in on someone else's, or trailing off before a partner cuts in. Feed it "Wait, before we get there, can I just say—" on its own and it reads that as a complete, self-contained sentence, because as far as that call is concerned, it is one. Stitch four of those independently-generated lines back together with silence gaps between them and the words alternate correctly while the delivery doesn't — no anticipation, no matched pacing, no sense that either voice is actually responding to the other. That's the tell that gives a chained dialogue away before a listener can even articulate why: two monologues taking turns is not the same thing as a conversation.
The symptoms are specific once you know to listen for them. Every line starts at roughly the same energy, because every call began from the same blank starting point regardless of whether the line before it was calm or heated. Gaps between speakers are either too uniform — the same silence stitched in every time, whatever the pacing — or built by hand and never quite match how people actually leave room for each other. And a line that's clearly a reaction ("wait, really?") gets read with the flat delivery of a standalone statement, because in isolation, that's all it ever was.
What one call that reads the whole script buys you
generate_multi_speaker_speech produces the entire exchange from a single generation instead of many. It takes the full script as one block of text, with each line attributed to a speaker alias, and a speakers parameter assigning a distinct voice to each alias used — the output is spoken in the order the lines appear, but generated as one continuous pass rather than assembled from fragments that never knew about each other. That's the actual mechanical difference turn-taking depends on: pacing and rhythm get modeled across the whole exchange in a single pass, rather than reconstructed after the fact from pieces generated in isolation and glued together with silence.
It's worth being precise about what that does and doesn't produce. This isn't simulating two people literally talking over each other with overlapping audio — it's a scripted, turn-based conversation, one line following the next. What it fixes is the flatness that chaining introduces: pacing that holds together across a turn rather than resetting to a neutral baseline every time a new isolated call starts from a blank slate.
Where this matters and where it's overkill
Reach for the multi-speaker tool specifically when a script has two or more distinct voices in it — an interview, a scripted back-and-forth, a two-host podcast intro, a debate or panel format. For a single narrator reading a single script, it's the wrong tool entirely: generate_speech is the simpler, more direct option, and adding a multi-speaker call for one voice buys nothing but an unnecessary parameter to fill in. The dividing line isn't complexity of the script — a long monologue is still a generate_speech job — it's whether more than one distinct voice needs to be present in the same piece of audio.
A borderline case worth naming: a single narrator reading quoted dialogue — "and then she said, 'wait, really?'" — is still one voice and still belongs to generate_speech, even though the script technically contains two speakers on the page. The tool cares about how many distinct voices need to come out of the generation, not how many characters appear in the text being read.
Scripting it so the assignment isn't ambiguous
The whole mechanism depends on a script that's unambiguous about who's speaking, line by line — write it with a clear alias prefix on every line ("Host:", "Guest:") so the order and the speaker assignment can't be misread, and make sure every alias used in the script has a matching entry in the speakers parameter, since an alias without one leaves the assignment undefined. language and style_prompt steer the delivery — pacing, formality, overall energy — but it's worth knowing that's a single setting applied across the whole exchange rather than a per-speaker dial. If one voice needs to read notably more formal than the other, that's a distinction to write into the script itself — pacing and word choice per line — rather than something a global style setting can split apart on its own.
Getting it onto video afterward
generate_multi_speaker_speech produces audio only — the dialogue track itself, nothing else. Getting it onto a video is a separate, second step: feed the resulting file into attach_audio_to_video, choosing mode replace if the clip's existing sound should be dropped entirely or mix if there's an ambient bed or existing sound worth keeping underneath the new dialogue. Anything visual that needs to track the audio — on-screen captions, lipsync matched per speaker — is its own separate pass on top of that, not something the dialogue generation step handles by itself.
What it costs to generate the whole exchange at once
Billing follows script length and speaker count together, priced and shown before the generation runs rather than after — worth checking with estimate_cost ahead of a genuinely long scripted conversation rather than assuming a five-minute two-hander costs the same as a thirty-second exchange. That's a straightforward trade against the chained approach, not a hidden cost: one multi-speaker generation replaces however many individual generate_speech calls the chained version would have needed, and it's priced as the one job it actually is rather than as a stack of separate ones that happen to add up to a conversation.
A Versely walkthrough
A two-line exchange makes the difference obvious fast. Given a script like:
Host: So today we're talking about the thing everyone's been asking about. Guest: Wait, before we get there — can I just say I think people are overthinking this? Host: Go for it.
the request that generates it as one coherent exchange looks like:
"Generate this two-speaker dialogue as one clip — Alex as Host, Priya as Guest, casual and quick, like they're genuinely riffing off each other. Then attach it to my intro clip, mixing with the ambient bed that's already there."
That's generate_multi_speaker_speech with both aliases mapped to distinct voices and a style_prompt describing the casual, quick energy for the exchange as a whole, followed by attach_audio_to_video in mix mode so the existing ambience survives underneath the new dialogue. Compare that same script run as three separate generate_speech calls stitched together, and the words match exactly — it's the rhythm between "can I just say" and "go for it" that's audibly missing. For the broader picture of what to reach for beyond a two-line example, Versely's guide to adding multi-speaker dialogue to a video and the multi-voice dialogue capability cover the longer scripted-conversation and podcast-clip cases directly, and Versely's voice-over hub and best text-to-speech model comparison are worth a look before picking voices for either speaker.