Narrate a Multi-character Story with Gemini and Text-to-Speech
Source notebook
Repo path:
audio/speech/use-cases/storytelling/storytelling.ipynb· Open on GitHub · intermediate
Generates a Gemini play script and narrates it with distinct Text-to-Speech voices per character.
Summary
This notebook teaches how to create a structured multi-character play with Gemini and turn it into an audio performance. It generates or loads a play, lists Chirp3 Text-to-Speech voices, maps each character to a voice, synthesizes each line as MP3, and combines the clips into one final audio file.
Key code patterns
Create Gemini client
from google import genai
PROJECT_ID = os.environ.get("GOOGLE_CLOUD_PROJECT")
LOCATION = os.environ.get("GOOGLE_CLOUD_REGION", "global")
client = genai.Client(enterprise=True, project=PROJECT_ID, location=LOCATION)Initializes the Gemini API on Agent Platform with project and location settings.
Structured story schema
class DialogueLine(BaseModel):
speaker: str
line: str
class Scene(BaseModel):
setting: str
dialogue: list[DialogueLine]
class Story(BaseModel):
title: str
characters: list[Character]
scenes: list[Scene]Defines the JSON structure Gemini must return for downstream voice assignment and audio synthesis.
Generate parsed story
response = client.models.generate_content(
model=MODEL_ID,
contents=PROMPT,
config=GenerateContentConfig(
system_instruction=SYSTEM_INSTRUCTION,
response_mime_type="application/json",
response_schema=Story,
),
)
story = response.parsedUses Gemini with a response schema so the generated play can be consumed as typed data.
Configure regional TTS client
api_endpoint = "texttospeech.googleapis.com"
if TTS_LOCATION != "global":
api_endpoint = f"{TTS_LOCATION}-{api_endpoint}"
tts_client = texttospeech.TextToSpeechClient(
client_options=ClientOptions(api_endpoint=api_endpoint)
)Builds a Text-to-Speech client that can target global or regional endpoints.
Synthesize one line per voice
response = tts_client.synthesize_speech(
input=texttospeech.SynthesisInput(text=text),
voice=texttospeech.VoiceSelectionParams(language_code=language_code, name=voice_name),
audio_config=texttospeech.AudioConfig(audio_encoding=texttospeech.AudioEncoding.MP3),
)
with open(output_file, "wb") as f:
f.write(response.audio_content)Handles the notebook’s constraint that each Text-to-Speech call uses one voice.
Models & APIs used
- Models: gemini-3.5-flash, en-GB-Chirp3-HD-Zubenelgenubi, en-GB-Chirp3-HD-Umbriel
- APIs / services: Gemini API in Vertex AI, Text-to-Speech, Cloud Storage
- SDKs / libraries:
google-genai,google-cloud-texttospeech,pydub,pandas,tqdm,pydantic
When to use this
Use this pattern when converting a generated multi-speaker script into narrated audio with separate voices per character.
Gotchas & caveats
- Requires Google Cloud authentication and setting the active gcloud project.
- Uses billable Gemini API in Vertex AI, Text-to-Speech, and Cloud Storage components.
- Text-to-Speech available regions must be checked before setting TTS_LOCATION.
- Text-to-Speech can only create audio with one voice per API call, so each line is synthesized separately.
- Mac users need FFmpeg installed for pydub MP3 handling.
- Voice assignment can fail if there are more characters than available voices.
Best practices
- Use response_schema with Gemini to produce structured output for downstream processing.
- Use a narrator voice for scene settings and title narration.
- Filter available voices before assigning them to characters.
- Use one Text-to-Speech call per dialogue line when different voices are required.
- Combine generated clips with short silence between lines and remove intermediate MP3 files.
Related
- Concepts: Audio & Speech · Gemini Capabilities · Applied Use Cases
- Entities: Vertex AI · Google GenAI SDK · Cloud Storage · Chirp · Gemini
- Area: Audio Notebooks
- Best practices: Audio & Speech - Best Practices · Gemini Capabilities - Best Practices · Applied Use Cases - Best Practices