The user's topic, voice selection, and duration are sent to DeepSeek-V4-Flash via GMI's LLM API with stream: true. Tokens arrive in real-time over an SSE connection and are forwarded to the browser. The model returns a structured JSON script — host/guest turns, company, guest name, title — in a single pass (~1–2 s).
02
Parallel Voice Synthesis
All N dialogue lines are dispatched to Inworld TTS-2 concurrently via Promise.all() — not sequentially. Each request returns an MP3 audio_url and word-level timestamps (timestamp_type: 'WORD') used for live transcript highlighting. Parallel dispatch cuts synthesis time from ~N×T to ~max(T).
03
Progressive Playback via SSE
The Node.js server streams token, meta, line, and done events to the browser via Server-Sent Events. The play button activates after the first 3 audio segments are preloaded — without waiting for the full episode. Lines are appended to the transcript progressively as TTS resolves.
04
Word-Level Transcript Sync
Inworld TTS-2 returns wordAlignment arrays: words[], wordStartTimeSeconds[], wordEndTimeSeconds[]. A requestAnimationFrame loop compares audio.currentTime against each word's start offset and applies the .spoken class for real-time highlighting.
// Synthesize a dialogue line with Inworld TTS-2const json = awaitfetch(
'https://console.gmicloud.ai/api/v1/ie/requestqueue/apikey/requests',
{
method: 'POST',
headers: { Authorization: `Bearer ${GMI_API_KEY}` },
body: JSON.stringify({
model: 'inworld-tts-2',
payload: {
text: line.text,
voice_id: 'Alex', // Alex | Dennis | Carter | Blake
audio_encoding: 'MP3',
sample_rate_hertz: 48000,
speaking_rate: 1.0,
temperature: 1.1,
timestamp_type: 'WORD', // enables word-level alignment
},
}),
}
).then(r => r.json());
// Response shapeconst audioUrl = json.outcome.audio_url;
const alignment = json.outcome.medias[0]
.timestampInfo.wordAlignment;
// → { words[], wordStartTimeSeconds[], wordEndTimeSeconds[] }// Fire all lines concurrently for parallel synthesisawait Promise.all(script.map(line => synthesize(line)));
// 25 English Inworld TTS-2 voices with persona metadata —// fed to the script LLM so it can match voice to character.const ENGLISH_VOICES = [
{ id: 'Alex', gender: 'male', description: 'Energetic mid-range male', tags: ['friendly', 'expressive'] },
{ id: 'Priya', gender: 'female', description: 'Female, Indian accent', tags: ['friendly', 'gentle'] },
{ id: 'Clive', gender: 'male', description: 'British male, calm', tags: ['calm', 'british'] },
// …22 more
];
// Auto mode: LLM picks voice IDs alongside the script in one call,// matching guest_gender and any accent cue in the guest's name.const hostVoice = resolveVoice(req.host_voice, meta.host_voice, 'male');
const guestVoice = resolveVoice(req.guest_voice, meta.guest_voice, meta.guest_gender);
// Preview a voice before recording (short TTS sample)
app.get('/api/preview-voice', async (req, res) => {
const { voice_id } = req.query;
const tts = awaitgenerateTTSLine(
`Hello, my name is ${voice_id}. Great to be here.`,
voice_id
);
res.json({ audio_url: tts.outcome?.audio_url });
});
// Browser: stream the script, then fan the lines out to TTS in parallelconst meta = awaitstreamScript({ topic, hostParam: 'Alex', guestParam: 'auto', duration: 1 }); // POST /api/script (SSE)const lines = await Promise.all(meta.script.map(l => synthesizeLine(l.text, l.speaker === 'HOST' ? meta.host_voice : meta.guest_voice))); // POST /api/tts
Build this on GMI Cloud
One endpoint. 200+ models. Pay per token, scale to zero.