You can use a Gemini TTS model (gemini-3.8-flash-tts or
gemini-3.8-flash-lite-tts) to synthesize natural, expressive speech (audio)
from text. When you use Firebase AI Logic, you can make this request
directly from your mobile or web app (such as your Apple (Swift), Android
(Kotlin / Java), Web (JavaScript), Flutter (Dart), or Unity app).
Text-to-speech (TTS) generation is controllable, meaning that you provide the exact text to synthesize into speech, while guiding the delivery—such as emotion, pacing, speaker voices, and non-speech sounds like laughter or sighs—using structured metadata and inline vocal tags. You can think of TTS as the opposite of transcription (speech-to-text).
With this capability, you can build features such as:
- Interactive storytelling and audiobooks: Narrate long-form stories or role-playing games where characters switch voices, react to each other with overlapping backchannels, and adapt their emotional delivery (like whispering in suspense or laughing at a joke).
- Low-latency voice agents and assistants: Stream synthesized speech as soon as text arrives from an LLM, keeping a consistent voice persona across conversational turns.
- Multilingual content readers and language tutors: Read articles, recipes, or study material aloud across more than 130 languages and regional accents, or switch languages seamlessly within a single conversation.
This guide shows how to generate single-speaker and multi-speaker speech, stream
audio responses, direct vocal delivery with SpeechMetadata and inline tags,
and choose from prebuilt, library, or custom voices.
Jump to single-speaker code Jump to multi-speaker code Jump to streaming code
Comparison between TTS and the Live API
Both text-to-speech (TTS) models and Live API models are low-latency, speech-generating models that can be configured for different response voices and languages. However, they serve very different use cases.
Text-to-speech (TTS) generation is a unidirectional, request-response interaction (text in, audio out). It's tailored for scenarios that require exact recitation of the provided text with fine-grained control over style and sound, such as podcast narration, audiobooks, or reading articles aloud.
Live API generation supports bidirectional streaming for real-time voice conversations (voice in, voice out). It excels in dynamic conversational contexts where the model decides the applicable speech to return. Note that the latest Live API models also support video and image input.
Before you begin
|
Click your Gemini API provider to view provider-specific content and code on this page. |
If you haven't already, complete the
getting started guide, which describes how to
set up your Firebase project, connect your app to Firebase, add the SDK,
initialize the backend service for your chosen Gemini API provider, and
create a GenerativeModel instance.
For testing and iterating on your prompts, we recommend using Google AI Studio.
Models that support this capability
gemini-3.8-flash-ttsgemini-3.8-flash-lite-tts
Comparison of TTS models
Both Gemini 3.x TTS models have the same request schema and prompting format. However, here are some differences for when you might use each model.
| Feature or workload | gemini-3.8-flash-tts |
gemini-3.8-flash-lite-tts |
|---|---|---|
| Primary strength | Studio-grade voice fidelity, acting nuance, and broad dialect coverage | High throughput, low latency, and cost efficiency |
| Best use cases | Audiobooks, studio narration, complex multi-speaker dialogue, frequent vocal-burst tags, difficult pronunciation, regional dialects | High-volume production, real-time voice agent cascades, read-aloud UI features, everyday single-speaker speech |
| Supported languages | 132 languages | 103 languages |
Generate speech from text
You can generate speech from text input in a single-speaker voice or a
two-speaker dialogue, either as a complete WAV file (generateContent) or as a
low-latency raw PCM audio stream (generateContentStream).
Generate speech with a single speaker
|
Before trying this sample, complete the
Before you begin section of this guide
to set up your project and app. In that section, you'll also click a button for your chosen Gemini API provider so that you see provider-specific content on this page. |
Here's how to generate single-speaker audio:
In your
GenerationConfig, setresponseModalitiestoAUDIOand configure aSpeechConfigwith the following:(Required) A voice name or ID. This can be a prebuilt voice like
Kore, an Extended Voice Library voice ID, or a custom voicevoice_.../voicekey_...ID.(Optional) A BCP-47 language code. If omitted, the model automatically detects the language.
Create a
TextPartwith the following:(Required) The verbatim transcript to be spoken.
(Optional) A
SpeechMetadatawith astyledescription (for example,"cheerful and friendly").
Call
generateContent. By default, unary requests return a complete WAV (audio/wav) audio file (16-bit linear PCM, 24 kHz, mono, with a standard 44-byte RIFF header) in the response's inline data parts.
Swift
import FirebaseAILogic
// Initialize the Gemini Developer API backend service.
let ai = FirebaseAI.firebaseAI(backend: .googleAI())
// Set `responseModalities` to include `.audio`.
// Configure a `SpeechConfig` with your chosen voice name (and optionally a language code).
let config = GenerationConfig(
responseModalities: [.audio],
speechConfig: SpeechConfig(voiceName: "Kore", languageCode: "en-US")
)
// Create a `GenerativeModel` instance with a model that supports speech generation.
let model = ai.generativeModel(
modelName: "gemini-3.8-flash-tts",
generationConfig: config
)
// Provide the verbatim text to synthesize as speech.
// Optionally provide turn-level style metadata.
let prompt = TextPart(
"Have a wonderful day!",
speechMetadata: SpeechMetadata(style: "cheerful and friendly")
)
// Call `generateContent` to generate the speech output.
let response = try await model.generateContent(prompt)
// Extract the WAV audio data and handle it for downstream use. For example:
for part in response.inlineDataParts {
let wavData = part.data // WAV audio bytes (24kHz, 1 channel, 16-bit PCM)
let mimeType = part.mimeType // for example: "audio/wav"
print("Received audio data with MIME type: \(mimeType)")
// Play the WAV audio data using your own playback helper (for example, with `AVAudioPlayer(data: wavData)`).
playAudio(data: wavData)
}
Kotlin
For Kotlin, the methods in this SDK are suspend functions and need to be called from a Coroutine scope.
// Set `responseModalities` to include `AUDIO`.
// Configure a `SpeechConfig` with your chosen voice name (and optionally a language code).
val config = generationConfig {
responseModalities = listOf(ResponseModality.AUDIO)
speechConfig = SpeechConfig(
voice = Voice("Kore"),
languageCode = "en-US"
)
}
// Initialize the Gemini Developer API backend service.
// Create a `GenerativeModel` instance with a model that supports speech generation.
val model = Firebase.ai(backend = GenerativeBackend.googleAI())
.generativeModel(
modelName = "gemini-3.8-flash-tts",
generationConfig = config
)
// Provide the verbatim text to synthesize as speech.
// Optionally provide turn-level style metadata.
val prompt = content {
text(
text = "Have a wonderful day!",
speechMetadata = SpeechMetadata(style = "cheerful and friendly")
)
}
// Call `generateContent` to generate the speech output.
val response = model.generateContent(prompt)
// Extract the WAV audio data and handle it for downstream use. For example:
val part = response.candidates.firstOrNull()?.content?.parts?.firstOrNull()
if (part is InlineDataPart) {
val wavData = part.inlineData // WAV audio bytes (24kHz, 1 channel, 16-bit PCM)
// Pass the audio data to your audio player or playback helper (which you'll need to implement).
playAudio(wavData)
}
Java
For Java, the streaming methods in this SDK return aPublisher type from the Reactive Streams library.
// Set `responseModalities` to include `AUDIO`.
// Configure a `SpeechConfig` with your chosen voice name (and optionally a language code).
GenerationConfig config = new GenerationConfig.Builder()
.setResponseModalities(Collections.singletonList(ResponseModality.AUDIO))
.setSpeechConfig(new SpeechConfig(new Voice("Kore"), "en-US"))
.build();
// Initialize the Gemini Developer API backend service.
// Create a `GenerativeModel` instance with a model that supports speech generation.
GenerativeModel ai = FirebaseAI.getInstance(GenerativeBackend.googleAI())
.generativeModel("gemini-3.8-flash-tts", config);
// Use the GenerativeModelFutures Java compatibility layer.
GenerativeModelFutures model = GenerativeModelFutures.from(ai);
// Provide the verbatim text to synthesize as speech.
// Optionally provide turn-level style metadata.
Content content = new Content.Builder()
.addText("Have a wonderful day!", new SpeechMetadata(null, "cheerful and friendly"))
.build();
Executor executor = Executors.newSingleThreadExecutor();
// Call `generateContent` to generate the speech output.
// Extract the WAV audio data and handle it for downstream use.
ListenableFuture<GenerateContentResponse> response = model.generateContent(content);
Futures.addCallback(response, new FutureCallback<GenerateContentResponse>() {
@Override
public void onSuccess(GenerateContentResponse result) {
Part part = result.getCandidates().get(0).getContent().getParts().get(0);
if (part instanceof InlineDataPart) {
byte[] wavData = ((InlineDataPart) part).getInlineData(); // WAV audio bytes (24kHz, 1 channel, 16-bit PCM)
// Pass the audio data to your audio player or playback helper (which you'll need to implement).
playAudio(wavData);
}
}
@Override
public void onFailure(Throwable t) {
t.printStackTrace();
}
}, executor);
Web
import { initializeApp } from "firebase/app";
import { getAI, getGenerativeModel, GoogleAIBackend, ResponseModality } from "firebase/ai";
// TODO(developer): Replace with your app's Firebase configuration
const firebaseConfig = { /* ... */ };
const firebaseApp = initializeApp(firebaseConfig);
// Initialize the Gemini Developer API backend service.
const ai = getAI(firebaseApp, { backend: new GoogleAIBackend() });
// Set `responseModalities` to include `AUDIO`.
// Configure a `SpeechConfig` with your chosen voice name (and optionally a language code).
const generationConfig = {
responseModalities: [ResponseModality.AUDIO],
speechConfig: {
voiceConfig: { prebuiltVoiceConfig: { voiceName: "Kore" } },
languageCode: "en-US"
}
};
// Create a `GenerativeModel` instance with a model that supports speech generation.
const model = getGenerativeModel(ai, {
model: "gemini-3.8-flash-tts",
generationConfig
});
// Provide the verbatim text to synthesize as speech.
// Optionally provide turn-level style metadata.
const prompt = [
{
type: "text",
text: "Have a wonderful day!",
speechMetadata: { style: "cheerful and friendly" }
}
];
// Call `generateContent` to generate the speech output.
const result = await model.generateContent(prompt);
const inlineDataParts = result.response.inlineDataParts();
// Extract the WAV audio data and handle it for downstream use. For example:
if (inlineDataParts?.[0]) {
const wavBase64 = inlineDataParts[0].inlineData.data; // WAV audio bytes (24kHz, 1 channel, 16-bit PCM)
// Decode base64 to ArrayBuffer
const wavBuffer = Uint8Array.from(atob(wavBase64), c => c.charCodeAt(0)).buffer;
// The response is a complete WAV file, so the Web Audio API can decode and play it directly.
const audioCtx = new AudioContext();
const audioBuffer = await audioCtx.decodeAudioData(wavBuffer);
const source = audioCtx.createBufferSource();
source.buffer = audioBuffer;
source.connect(audioCtx.destination);
source.start();
}
Dart
import 'package:firebase_ai/firebase_ai.dart';
// Set `responseModalities` to include `audio`.
// Configure a `SpeechConfig` with your chosen voice name (and optionally a language code).
final config = GenerationConfig(
responseModalities: [ResponseModalities.audio],
speechConfig: SpeechConfig(voiceName: 'Kore', languageCode: 'en-US'),
);
// Initialize the Gemini Developer API backend service.
// Create a `GenerativeModel` instance with a model that supports speech generation.
final model = FirebaseAI.googleAI().generativeModel(
model: 'gemini-3.8-flash-tts',
generationConfig: config,
);
// Provide the verbatim text to synthesize as speech.
// Optionally provide turn-level style metadata.
final prompt = Content.multi([
TextPart(
'Have a wonderful day!',
speechMetadata: SpeechMetadata(style: 'cheerful and friendly'),
),
]);
// Call `generateContent` to generate the speech output.
final response = await model.generateContent([prompt]);
// Extract the WAV audio data and handle it for downstream use. For example:
final part = response.candidates.first.content.parts.first;
if (part is InlineDataPart && part.mimeType.startsWith('audio/')) {
final Uint8List wavData = part.bytes; // WAV audio bytes (24kHz, 1 channel, 16-bit PCM)
// Play the WAV audio data using your preferred audio player or helper (which you'll need to implement).
await playAudio(wavData);
}
Unity
using Firebase.AI;
// Initialize the Gemini Developer API backend service.
var ai = FirebaseAI.GetInstance(FirebaseAI.Backend.GoogleAI());
// Set `responseModalities` to include `Audio`.
// Configure a `SpeechConfig` with your chosen voice name and language code.
var config = new GenerationConfig(
responseModalities: new System.Collections.Generic.List<ResponseModality> { ResponseModality.Audio },
speechConfig: SpeechConfig.UsePrebuiltVoice("Kore", "en-US")
);
// Create a `GenerativeModel` instance with a model that supports speech generation.
var model = ai.GetGenerativeModel(
modelName: "gemini-3.8-flash-tts",
generationConfig: config
);
// Provide the verbatim text to synthesize as speech.
// Optionally provide turn-level style metadata.
var prompt = new ModelContent(
new ModelContent.TextPart(
"Have a wonderful day!",
new SpeechMetadata(speaker: null, style: "cheerful and friendly")
)
);
// Call `GenerateContentAsync` to generate the speech output.
var response = await model.GenerateContentAsync(prompt);
// Extract the WAV audio data and handle it for downstream use. For example:
if (response.Candidates.Count > 0) {
foreach (var part in response.Candidates[0].Content.Parts) {
if (part is ModelContent.InlineDataPart inlineData) {
byte[] wavData = inlineData.Data; // WAV audio bytes (24kHz, 1 channel, 16-bit PCM)
// Pass the audio data to your audio player or playback helper (which you'll need to implement).
PlayAudio(wavData);
}
}
}
Generate speech with multiple speakers
|
Before trying this sample, complete the
Before you begin section of this guide
to set up your project and app. In that section, you'll also click a button for your chosen Gemini API provider so that you see provider-specific content on this page. |
You can generate a natural two-speaker conversation in a single request by
assigning each speaker a distinct voice and passing each line of dialogue as its
own TextPart:
Create a
MultiSpeakerVoiceConfigthat maps two speaker names (for example,"Joe"and"Jane") to theirSpeakerVoiceConfigvoices.In your
GenerationConfig, setresponseModalitiestoAUDIOand pass yourMultiSpeakerVoiceConfig(and optionallanguageCode) intoSpeechConfig.Provide an array or list of
TextPartobjects, specifically oneTextPartper dialogue turn. On everyTextPart, setSpeechMetadatawith the following:- (Required) A
speakerthat matches one of the speaker names defined in yourMultiSpeakerVoiceConfig. - (Optional) A
styledescription (for example,"cheerful and friendly").
- (Required) A
Call
generateContent. The model synthesizes the full conversation and returns a complete WAV (audio/wav) audio file.
Swift
import FirebaseAILogic
// Initialize the Gemini Developer API backend service.
let ai = FirebaseAI.firebaseAI(backend: .googleAI())
// Configure a `SpeechConfig` for multiple speakers, assigning a voice to each speaker.
let multiSpeechConfig = SpeechConfig(
multiSpeakerVoiceConfig: MultiSpeakerVoiceConfig(
speakerVoiceConfigs: [
SpeakerVoiceConfig(speaker: "Joe", voiceName: "Puck"),
SpeakerVoiceConfig(speaker: "Jane", voiceName: "Kore")
]
),
languageCode: "en-US"
)
// Set `responseModalities` to include `.audio`.
let config = GenerationConfig(
responseModalities: [.audio],
speechConfig: multiSpeechConfig
)
// Create a `GenerativeModel` instance with a model that supports speech generation.
let model = ai.generativeModel(
modelName: "gemini-3.8-flash-tts",
generationConfig: config
)
// Provide one `TextPart` per dialogue turn, specifying the speaker and optional style in `SpeechMetadata`.
let prompt: [any PartsRepresentable] = [
TextPart(
"How's it going today, Jane?",
speechMetadata: SpeechMetadata(speaker: "Joe", style: "cheerful and friendly")
),
TextPart(
"Not too bad, how about you?",
speechMetadata: SpeechMetadata(speaker: "Jane", style: "calm and relaxed")
)
]
// Call `generateContent` to generate the multi-speaker speech output.
let response = try await model.generateContent(prompt)
// Extract the WAV audio data and handle it for downstream use. For example:
for part in response.inlineDataParts {
let wavData = part.data // WAV audio bytes (24kHz, 1 channel, 16-bit PCM)
let mimeType = part.mimeType // for example: "audio/wav"
print("Received audio data with MIME type: \(mimeType)")
// Play the WAV audio data using your own playback helper (for example, with `AVAudioPlayer(data: wavData)`).
playAudio(data: wavData)
}
Kotlin
For Kotlin, the methods in this SDK are suspend functions and need to be called from a Coroutine scope.
// Configure a `SpeechConfig` for multiple speakers, assigning a voice to each speaker.
val multiSpeechConfig = SpeechConfig(
multiSpeakerVoiceConfig = MultiSpeakerVoiceConfig(
speakerVoiceConfigs = listOf(
SpeakerVoiceConfig(speaker = "Joe", voice = Voice("Puck")),
SpeakerVoiceConfig(speaker = "Jane", voice = Voice("Kore"))
)
),
languageCode = "en-US"
)
// Set `responseModalities` to include `AUDIO`.
val config = generationConfig {
responseModalities = listOf(ResponseModality.AUDIO)
speechConfig = multiSpeechConfig
}
// Initialize the Gemini Developer API backend service.
// Create a `GenerativeModel` instance with a model that supports speech generation.
val model = Firebase.ai(backend = GenerativeBackend.googleAI())
.generativeModel(
modelName = "gemini-3.8-flash-tts",
generationConfig = config
)
// Provide one text part per dialogue turn, specifying the speaker and optional style in `SpeechMetadata`.
val prompt = content {
text(
text = "How's it going today, Jane?",
speechMetadata = SpeechMetadata(speaker = "Joe", style = "cheerful and friendly")
)
text(
text = "Not too bad, how about you?",
speechMetadata = SpeechMetadata(speaker = "Jane", style = "calm and relaxed")
)
}
// Call `generateContent` to generate the multi-speaker speech output.
val response = model.generateContent(prompt)
// Extract the WAV audio data and handle it for downstream use. For example:
val part = response.candidates.firstOrNull()?.content?.parts?.firstOrNull()
if (part is InlineDataPart) {
val wavData = part.inlineData // WAV audio bytes (24kHz, 1 channel, 16-bit PCM)
// Pass the audio data to your audio player or playback helper (which you'll need to implement).
playAudio(wavData)
}
Java
For Java, the streaming methods in this SDK return aPublisher type from the Reactive Streams library.
// Configure a `SpeechConfig` for multiple speakers, assigning a voice to each speaker.
MultiSpeakerVoiceConfig multiSpeakerVoiceConfig = new MultiSpeakerVoiceConfig(
Arrays.asList(
new SpeakerVoiceConfig("Joe", new Voice("Puck")),
new SpeakerVoiceConfig("Jane", new Voice("Kore"))
)
);
SpeechConfig multiSpeechConfig = new SpeechConfig(multiSpeakerVoiceConfig, "en-US");
// Set `responseModalities` to include `AUDIO`.
GenerationConfig config = new GenerationConfig.Builder()
.setResponseModalities(Collections.singletonList(ResponseModality.AUDIO))
.setSpeechConfig(multiSpeechConfig)
.build();
// Initialize the Gemini Developer API backend service.
// Create a `GenerativeModel` instance with a model that supports speech generation.
GenerativeModel ai = FirebaseAI.getInstance(GenerativeBackend.googleAI())
.generativeModel("gemini-3.8-flash-tts", config);
// Use the `GenerativeModelFutures` Java compatibility layer.
GenerativeModelFutures model = GenerativeModelFutures.from(ai);
// Provide one text part per dialogue turn, specifying the speaker and optional style in `SpeechMetadata`.
Content content = new Content.Builder()
.addText(
"How's it going today, Jane?",
new SpeechMetadata("Joe", "cheerful and friendly"))
.addText(
"Not too bad, how about you?",
new SpeechMetadata("Jane", "calm and relaxed"))
.build();
Executor executor = Executors.newSingleThreadExecutor();
// Call `generateContent` to generate the multi-speaker speech output.
// Extract the WAV audio data and handle it for downstream use.
ListenableFuture<GenerateContentResponse> response = model.generateContent(content);
Futures.addCallback(response, new FutureCallback<GenerateContentResponse>() {
@Override
public void onSuccess(GenerateContentResponse result) {
Part part = result.getCandidates().get(0).getContent().getParts().get(0);
if (part instanceof InlineDataPart) {
byte[] wavData = ((InlineDataPart) part).getInlineData(); // WAV audio bytes (24kHz, 1 channel, 16-bit PCM)
// Pass the audio data to your audio player or playback helper (which you'll need to implement).
playAudio(wavData);
}
}
@Override
public void onFailure(Throwable t) {
t.printStackTrace();
}
}, executor);
Web
import { initializeApp } from "firebase/app";
import { getAI, getGenerativeModel, GoogleAIBackend, ResponseModality } from "firebase/ai";
// TODO(developer): Replace with your app's Firebase configuration
const firebaseConfig = { /* ... */ };
const firebaseApp = initializeApp(firebaseConfig);
// Initialize the Gemini Developer API backend service.
const ai = getAI(firebaseApp, { backend: new GoogleAIBackend() });
// Set `responseModalities` to include `AUDIO`.
// Configure a `SpeechConfig` for multiple speakers, assigning a voice to each speaker.
const generationConfig = {
responseModalities: [ResponseModality.AUDIO],
speechConfig: {
multiSpeakerVoiceConfig: {
speakerVoiceConfigs: [
{ speaker: "Joe", voiceConfig: { prebuiltVoiceConfig: { voiceName: "Puck" } } },
{ speaker: "Jane", voiceConfig: { prebuiltVoiceConfig: { voiceName: "Kore" } } }
]
},
languageCode: "en-US"
}
};
// Create a `GenerativeModel` instance with a model that supports speech generation.
const model = getGenerativeModel(ai, {
model: "gemini-3.8-flash-tts",
generationConfig
});
// Provide one `TextPart` per dialogue turn, specifying the speaker and optional style in `speechMetadata`.
const prompt = [
{
type: "text",
text: "How's it going today, Jane?",
speechMetadata: { speaker: "Joe", style: "cheerful and friendly" }
},
{
type: "text",
text: "Not too bad, how about you?",
speechMetadata: { speaker: "Jane", style: "calm and relaxed" }
}
];
// Call `generateContent` to generate the multi-speaker speech output.
const result = await model.generateContent(prompt);
const inlineDataParts = result.response.inlineDataParts();
// Extract the WAV audio data and handle it for downstream use. For example:
if (inlineDataParts?.[0]) {
const wavBase64 = inlineDataParts[0].inlineData.data; // WAV audio bytes (24kHz, 1 channel, 16-bit PCM)
// Decode base64 to ArrayBuffer
const wavBuffer = Uint8Array.from(atob(wavBase64), c => c.charCodeAt(0)).buffer;
// The response is a complete WAV file, so the Web Audio API can decode and play it directly.
const audioCtx = new AudioContext();
const audioBuffer = await audioCtx.decodeAudioData(wavBuffer);
const source = audioCtx.createBufferSource();
source.buffer = audioBuffer;
source.connect(audioCtx.destination);
source.start();
}
Dart
import 'package:firebase_ai/firebase_ai.dart';
// Configure a `SpeechConfig` for multiple speakers, assigning a voice to each speaker.
final multiSpeechConfig = SpeechConfig.multiSpeaker(
multiSpeakerVoiceConfig: MultiSpeakerVoiceConfig(
speakerVoiceConfigs: [
SpeakerVoiceConfig(speaker: 'Joe', voiceName: 'Puck'),
SpeakerVoiceConfig(speaker: 'Jane', voiceName: 'Kore'),
],
),
languageCode: 'en-US',
);
// Set `responseModalities` to include `audio`.
final config = GenerationConfig(
responseModalities: [ResponseModalities.audio],
speechConfig: multiSpeechConfig,
);
// Initialize the Gemini Developer API backend service.
// Create a `GenerativeModel` instance with a model that supports speech generation.
final model = FirebaseAI.googleAI().generativeModel(
model: 'gemini-3.8-flash-tts',
generationConfig: config,
);
// Provide one `TextPart` per dialogue turn, specifying the speaker and optional style in `SpeechMetadata`.
final prompt = Content.multi([
TextPart(
"How's it going today, Jane?",
speechMetadata: SpeechMetadata(speaker: 'Joe', style: 'cheerful and friendly'),
),
TextPart(
'Not too bad, how about you?',
speechMetadata: SpeechMetadata(speaker: 'Jane', style: 'calm and relaxed'),
),
]);
// Call `generateContent` to generate the multi-speaker speech output.
final response = await model.generateContent([prompt]);
// Extract the WAV audio data and handle it for downstream use. For example:
final part = response.candidates.first.content.parts.first;
if (part is InlineDataPart && part.mimeType.startsWith('audio/')) {
final Uint8List wavData = part.bytes; // WAV audio bytes (24kHz, 1 channel, 16-bit PCM)
// Play the WAV audio data using your preferred audio player or helper (which you'll need to implement).
await playAudio(wavData);
}
Unity
using Firebase.AI;
// Initialize the Gemini Developer API backend service.
var ai = FirebaseAI.GetInstance(FirebaseAI.Backend.GoogleAI());
// Configure a `SpeechConfig` for multiple speakers, assigning a voice to each speaker.
var multiSpeakerVoiceConfig = new MultiSpeakerVoiceConfig(
new System.Collections.Generic.List<SpeakerVoiceConfig> {
SpeakerVoiceConfig.UsePrebuiltVoice("Joe", "Puck"),
SpeakerVoiceConfig.UsePrebuiltVoice("Jane", "Kore")
}
);
var multiSpeechConfig = SpeechConfig.UseMultiSpeakerVoice(multiSpeakerVoiceConfig, "en-US");
// Set `responseModalities` to include `Audio`.
var config = new GenerationConfig(
responseModalities: new System.Collections.Generic.List<ResponseModality> { ResponseModality.Audio },
speechConfig: multiSpeechConfig
);
// Create a `GenerativeModel` instance with a model that supports speech generation.
var model = ai.GetGenerativeModel(
modelName: "gemini-3.8-flash-tts",
generationConfig: config
);
// Provide one `TextPart` per dialogue turn, specifying the speaker and optional style in `SpeechMetadata`.
var prompt = new ModelContent(
new ModelContent.TextPart(
"How's it going today, Jane?",
new SpeechMetadata(speaker: "Joe", style: "cheerful and friendly")
),
new ModelContent.TextPart(
"Not too bad, how about you?",
new SpeechMetadata(speaker: "Jane", style: "calm and relaxed")
)
);
// Call `GenerateContentAsync` to generate the multi-speaker speech output.
var response = await model.GenerateContentAsync(prompt);
// Extract the WAV audio data and handle it for downstream use. For example:
if (response.Candidates.Count > 0) {
foreach (var part in response.Candidates[0].Content.Parts) {
if (part is ModelContent.InlineDataPart inlineData) {
byte[] wavData = inlineData.Data; // WAV audio bytes (24kHz, 1 channel, 16-bit PCM)
// Pass the audio data to your audio player or playback helper (which you'll need to implement).
PlayAudio(wavData);
}
}
}
Stream the response
|
Before trying this sample, complete the
Before you begin section of this guide
to set up your project and app. In that section, you'll also click a button for your chosen Gemini API provider so that you see provider-specific content on this page. |
For lower time-to-first-audio (TTFA) and real-time playback, you can stream the audio response as it's generated instead of waiting for the entire clip to finish. Streaming works with both single-speaker and multi-speaker configurations.
To stream speech, call generateContentStream instead of generateContent.
Unlike unary requests (which return a WAV file), streaming requests return
headerless raw 16-bit signed little-endian PCM chunks (audio/l16, 24 kHz,
mono) so you can append each chunk directly to your audio output buffer.
Swift
import FirebaseAILogic
// Initialize the Gemini Developer API backend service.
let ai = FirebaseAI.firebaseAI(backend: .googleAI())
// Set `responseModalities` to include `.audio`.
// Configure a `SpeechConfig` with your chosen voice name (and optionally a language code).
let config = GenerationConfig(
responseModalities: [.audio],
speechConfig: SpeechConfig(voiceName: "Kore")
)
// Create a `GenerativeModel` instance with a model that supports speech generation.
let model = ai.generativeModel(
modelName: "gemini-3.8-flash-tts",
generationConfig: config
)
// Provide the verbatim text to synthesize as speech.
// Optionally provide turn-level style metadata.
let prompt = TextPart(
"Tell me a story about a brave knight.",
speechMetadata: SpeechMetadata(style: "warm, engaging audiobook narrator")
)
// Call `generateContentStream` to stream the speech output as it is generated.
let responseStream = try model.generateContentStream(prompt)
// Extract the raw PCM audio chunks and queue them for playback:
for try await chunk in responseStream {
for part in chunk.inlineDataParts {
let pcmChunk = part.data // Headerless raw PCM bytes (24kHz, 1 channel, 16-bit)
// Append the audio chunk to your audio queue/buffer for playback (using a helper you implement).
appendAudioChunk(pcmChunk)
}
}
Kotlin
For Kotlin, the methods in this SDK are suspend functions and need to be called from a Coroutine scope.
// Set `responseModalities` to include `AUDIO`.
// Configure a `SpeechConfig` with your chosen voice name (and optionally a language code).
val config = generationConfig {
responseModalities = listOf(ResponseModality.AUDIO)
speechConfig = SpeechConfig(voice = Voice("Kore"))
}
// Initialize the Gemini Developer API backend service.
// Create a `GenerativeModel` instance with a model that supports speech generation.
val model = Firebase.ai(backend = GenerativeBackend.googleAI())
.generativeModel(
modelName = "gemini-3.8-flash-tts",
generationConfig = config
)
// Provide the verbatim text to synthesize as speech.
// Optionally provide turn-level style metadata.
val prompt = content {
text(
text = "Tell me a story about a brave knight.",
speechMetadata = SpeechMetadata(style = "warm, engaging audiobook narrator")
)
}
// Call `generateContentStream` to stream the speech output as it is generated.
// Extract the raw PCM audio chunks and queue them for playback.
model.generateContentStream(prompt).collect { chunk ->
val part = chunk.candidates.firstOrNull()?.content?.parts?.firstOrNull()
if (part is InlineDataPart) {
val pcmChunk = part.inlineData // Headerless raw PCM bytes (24kHz, 1 channel, 16-bit)
// Append the audio chunk to your audio queue/buffer for playback (using a helper you implement).
appendAudioChunk(pcmChunk)
}
}
Java
For Java, the streaming methods in this SDK return aPublisher type from the Reactive Streams library.
// Set `responseModalities` to include `AUDIO`.
// Configure a `SpeechConfig` with your chosen voice name (and optionally a language code).
GenerationConfig config = new GenerationConfig.Builder()
.setResponseModalities(Collections.singletonList(ResponseModality.AUDIO))
.setSpeechConfig(new SpeechConfig(new Voice("Kore")))
.build();
// Initialize the Gemini Developer API backend service.
// Create a `GenerativeModel` instance with a model that supports speech generation.
GenerativeModel ai = FirebaseAI.getInstance(GenerativeBackend.googleAI())
.generativeModel("gemini-3.8-flash-tts", config);
// Use the `GenerativeModelFutures` Java compatibility layer.
GenerativeModelFutures model = GenerativeModelFutures.from(ai);
// Provide the verbatim text to synthesize as speech.
// Optionally provide turn-level style metadata.
Content content = new Content.Builder()
.addText(
"Tell me a story about a brave knight.",
new SpeechMetadata(null, "warm, engaging audiobook narrator"))
.build();
// Call `generateContentStream` to stream the speech output as it is generated.
Publisher<GenerateContentResponse> streamingResponse =
model.generateContentStream(content);
// Extract the raw PCM audio chunks and queue them for playback.
streamingResponse.subscribe(new Subscriber<GenerateContentResponse>() {
@Override
public void onSubscribe(Subscription s) {
s.request(Long.MAX_VALUE);
}
@Override
public void onNext(GenerateContentResponse chunk) {
Part part = chunk.getCandidates().get(0).getContent().getParts().get(0);
if (part instanceof InlineDataPart) {
byte[] pcmChunk = ((InlineDataPart) part).getInlineData(); // Headerless raw PCM bytes (24kHz, 1 channel, 16-bit)
// Append the audio chunk to your audio queue/buffer for playback (using a helper you implement).
appendAudioChunk(pcmChunk);
}
}
@Override
public void onComplete() {
// Audio stream complete.
}
@Override
public void onError(Throwable t) {
t.printStackTrace();
}
});
Web
import { initializeApp } from "firebase/app";
import { getAI, getGenerativeModel, GoogleAIBackend, ResponseModality } from "firebase/ai";
// TODO(developer): Replace with your app's Firebase configuration
const firebaseConfig = { /* ... */ };
const firebaseApp = initializeApp(firebaseConfig);
// Initialize the Gemini Developer API backend service.
const ai = getAI(firebaseApp, { backend: new GoogleAIBackend() });
// Set `responseModalities` to include `AUDIO`.
// Configure a `SpeechConfig` with your chosen voice name (and optionally a language code).
const generationConfig = {
responseModalities: [ResponseModality.AUDIO],
speechConfig: {
voiceConfig: { prebuiltVoiceConfig: { voiceName: "Kore" } }
}
};
// Create a `GenerativeModel` instance with a model that supports speech generation.
const model = getGenerativeModel(ai, {
model: "gemini-3.8-flash-tts",
generationConfig
});
// Provide the verbatim text to synthesize as speech.
// Optionally provide turn-level style metadata.
const prompt = [
{
type: "text",
text: "Tell me a story about a brave knight.",
speechMetadata: { style: "warm, engaging audiobook narrator" }
}
];
// Call `generateContentStream` to stream the speech output as it is generated.
const result = await model.generateContentStream(prompt);
// Extract the raw PCM audio chunks and queue them for playback:
const playbackQueue = [];
for await (const chunk of result.stream) {
const inlineDataParts = chunk.inlineDataParts();
if (inlineDataParts?.[0]) {
const pcmBase64 = inlineDataParts[0].inlineData.data; // Headerless raw PCM bytes (24kHz, 1 channel, 16-bit)
const pcmBuffer = Uint8Array.from(atob(pcmBase64), c => c.charCodeAt(0)).buffer;
// Append the audio chunk to your audio queue/buffer for playback.
// In a real app, start playing chunks as they arrive (rather than waiting for
// the whole stream to finish) to get the low-latency benefit of streaming.
playbackQueue.push(pcmBuffer);
}
}
// To play back an array of PCM buffers in sequence, pass them to your playback helper (which you'll need to write).
processPlaybackQueue(playbackQueue);
Dart
import 'package:firebase_ai/firebase_ai.dart';
// Set `responseModalities` to include `audio`.
// Configure a `SpeechConfig` with your chosen voice name (and optionally a language code).
final config = GenerationConfig(
responseModalities: [ResponseModalities.audio],
speechConfig: SpeechConfig(voiceName: 'Kore'),
);
// Initialize the Gemini Developer API backend service.
// Create a `GenerativeModel` instance with a model that supports speech generation.
final model = FirebaseAI.googleAI().generativeModel(
model: 'gemini-3.8-flash-tts',
generationConfig: config,
);
// Provide the verbatim text to synthesize as speech.
// Optionally provide turn-level style metadata.
final prompt = Content.multi([
TextPart(
'Tell me a story about a brave knight.',
speechMetadata: SpeechMetadata(style: 'warm, engaging audiobook narrator'),
),
]);
// Call `generateContentStream` to stream the speech output as it is generated.
final responseStream = model.generateContentStream([prompt]);
// Extract the raw PCM audio chunks and queue them for playback:
await for (final chunk in responseStream) {
final part = chunk.candidates.first.content.parts.first;
if (part is InlineDataPart && part.mimeType.startsWith('audio/')) {
final Uint8List pcmChunk = part.bytes; // Headerless raw PCM bytes (24kHz, 1 channel, 16-bit)
// Append the audio chunk to your audio queue/buffer for playback (using a helper you implement).
appendAudioChunk(pcmChunk);
}
}
Unity
using System.Collections.Generic;
using System.Linq;
using Firebase.AI;
// Initialize the Gemini Developer API backend service.
var ai = FirebaseAI.GetInstance(FirebaseAI.Backend.GoogleAI());
// Set `ResponseModalities` to include `Audio`.
// Configure a `SpeechConfig` with your chosen voice name (and optionally a language code).
var config = new GenerationConfig(
responseModalities: new List<ResponseModality> { ResponseModality.Audio },
speechConfig: SpeechConfig.UsePrebuiltVoice("Kore")
);
// Create a `GenerativeModel` instance with a model that supports speech generation.
var model = ai.GetGenerativeModel(
modelName: "gemini-3.8-flash-tts",
generationConfig: config
);
// Provide the verbatim text to synthesize as speech.
// Optionally provide turn-level style metadata.
var prompt = new ModelContent(
new ModelContent.TextPart(
"Tell me a story about a brave knight.",
new SpeechMetadata(speaker: null, style: "warm, engaging audiobook narrator")
)
);
// Call `GenerateContentStreamAsync` to stream the speech output as it is generated.
var responseStream = model.GenerateContentStreamAsync(prompt);
// Extract the raw PCM audio chunks and queue them for playback:
await foreach (var response in responseStream)
{
var audioParts = response.Candidates.FirstOrDefault().Content.Parts
.OfType<ModelContent.InlineDataPart>();
foreach (var part in audioParts)
{
byte[] pcmChunk = part.Data; // Headerless raw PCM bytes (24kHz, 1 channel, 16-bit)
// Append the audio chunk to your audio queue/buffer for playback (using a helper you implement).
AppendAudioChunk(pcmChunk);
}
}
Control speech output with metadata and inline tags
The Gemini 3.x TTS models treat the text inside each TextPart
strictly as a verbatim transcript to be spoken aloud. Do not write
narrative instructions (such as "Say cheerfully:") inside the text string, or
the model may read those words aloud.
You can direct the vocal performance using the following techniques:
Turn-level style and inline vocal tags
Split your performance directions by scope between SpeechMetadata and
inline angle-bracket tags (<...>).
| Scope | Where to place | What it controls | Examples |
|---|---|---|---|
|
Turn-level (sustained across the entire turn) |
SpeechMetadata.style
|
Emotion, persona, tone, inflection, pitch, and overall speaking rate |
"cheerful and friendly","whispered, nervous","speaking rapidly","out of breath","sarcastic"
|
|
Point-in-time (localized to a specific moment in the sentence) |
Inline in TextPart text using <...>
|
Non-speech human vocal bursts, breaths, pauses, or momentary delivery shifts |
"Wait... <short pause> did you hear that? <gasp> Someone's at the door."
|
Pacing, pauses, pitch, and emphasis
Punctuation and ellipses: Use commas, dashes (
--), and ellipses (...) in the transcript to create natural phrasing and hesitation.Explicit pause tags: Insert
<short pause>or<long pause>inline at the exact point where the speaker should pause:Hold on, let me think... <short pause> Alright, I've got it.Speaking rate and pitch: Set
styleinSpeechMetadatato control the overall pace, pitch, or inflection for the turn (for example,"speaking rapidly and excitedly","speaking slowly", or"monotone and flat"). If the emotion or pace shifts mid-monologue, split the script into separateTextPartturns, each with its ownstyle.Word emphasis: Capitalize specific words in the transcript to stress them:
It was a VERY long day <sigh> ... nobody listens anymore.
Vocal bursts and non-speech sounds
Insert non-speech human vocalizations inline using angle brackets (<...>)
at the exact word boundary where the sound should occur. Use human vocalizations
rather than non-vocal sound effects. Here are recommended tags to use:
<argh> |
<exhales> |
<heavy breath> |
<scream> |
<snort> |
<breath> |
<gasp> |
<hiss> |
<short pause> |
<sob> |
<cackle> |
<giggle> |
<laugh> /<laughter> |
<shout> |
<throat-clearing> |
<cheer> |
<groan> |
<long pause> |
<shriek> |
<tsk> |
<chuckle> /<chuckles> |
<growl> |
<moan> |
<sigh> /<sighs> |
<whimper> |
<cough> |
<grr> |
<pant> |
<sneeze> |
<whispers> /<whispering> |
<cry> |
<grunt> |
<pff> /<phew> |
<snicker> |
<yawn> |
Backchannels and overlapping speech
In multi-speaker dialogue, you can wrap brief listener
reactions in pipe characters (|reaction|) inside the active speaker's
transcript. The non-active speaker interjects naturally at that point without
requiring a separate turn:
Short listener backchannels:
- Turn 1 (
speaker: "Joe"):"So the launch is Thursday |oh hmm| Are we actually ready?" - Turn 2 (
speaker: "Jane"):"Ready enough |oh really?| The last blocker cleared this morning."
- Turn 1 (
Overlapping speech: You can use multiple pipe segments to simulate simultaneous speech (works best with
gemini-3.8-flash-tts), such as:- Turn 1 (
speaker: "Joe"):"Let's surprise him on three |ok| ready?" - Turn 2 (
speaker: "Jane"):"one. two. three. |happy| happy |birthday| birthday!"
- Turn 1 (
Best practices for consistency
Test without
stylefirst, then add concisestyletweaks: Most requests sound natural with an emptystylefield. Add a shortstylephrase (like"casual, friendly"or"reflective and awe-inspired") only when a turn needs a specific delivery adjustment, and reuse that same short string across turns when you want a consistent baseline.Write natural spoken transcripts: Include conversational contractions and natural disfluencies (like
"Oh, uh, yeah I think... hm, that's interesting") directly in the transcript text.Avoid legacy
[Audio Profile]blocks and meta-instructions: Long persona paragraphs,[Director's Notes]blocks, or meta-instructions like"maintain identical timbre"increase voice drift. Instead, choose a matching voice from the Extended Voice Library or create a persistent character voice with Voice design.Don't try to override immutable voice traits in
style: Avoid usingSpeechMetadata.styleto change a voice's age, gender, or permanent accent. Instead, select or design a voice with those characteristics.
Supported voices
The Gemini 3.x TTS models support three ways to specify a voice:
All voices are multilingual, meaning you can use the same voice to generate
speech in any of the supported languages while keeping a
consistent speaker identity. For example, if you set the voice to the prebuilt
Kore voice and send prompts in Spanish, Hindi, and Vietnamese, the responses
all use the Kore voice in each of those languages. While the
30 core prebuilt voices are general-purpose multilingual
voices, the Extended Voice Library voices and the
custom voices are anchored to a primary locale or regional
accent. This means that they can still speak other languages, but they sound
most authentic in their primary locale.
Core prebuilt voices
The Gemini TTS models support 30 core prebuilt voices (such as Kore,
Puck, Zephyr, Charon, and Fenrir), each with distinct vocal
characteristics. Pass the voice name in SpeechConfig (as shown in the
single-speaker and multi-speaker examples
earlier on this page).
| Voice name | Description | Gender | Demo |
|---|---|---|---|
Achernar |
Soft | Female | |
Achird |
Friendly | Male | |
Algenib |
Gravelly | Male | |
Algieba |
Smooth | Male | |
Alnilam |
Firm | Male | |
Aoede |
Breezy | Female | |
Autonoe |
Bright | Female | |
Callirrhoe |
Easy-going | Female | |
Charon |
Informative | Male | |
Despina |
Smooth | Female | |
Enceladus |
Breathy | Male | |
Erinome |
Clear | Female | |
Fenrir |
Excitable | Male | |
Gacrux |
Mature | Female | |
Iapetus |
Clear | Male | |
Kore |
Firm | Female | |
Laomedeia |
Upbeat | Female | |
Leda |
Youthful | Female | |
Orus |
Firm | Male | |
Puck |
Upbeat | Male | |
Pulcherrima |
Forward | Female | |
Rasalgethi |
Informative | Male | |
Sadachbia |
Lively | Male | |
Sadaltager |
Knowledgeable | Male | |
Schedar |
Even | Male | |
Sulafat |
Warm | Female | |
Umbriel |
Easy-going | Male | |
Vindemiatrix |
Gentle | Female | |
Zephyr |
Bright | Female | |
Zubenelgenubi |
Casual | Male |
Preset voices from the Extended Voice Library
Beyond the 30 prebuilt voices, the Extended Voice Library provides more than 2,000 curated preset voices spanning diverse languages, regional accents, age ranges, character personas, and use cases (such as audiobooks, customer support agents, and newsreaders).
Here's how to use any voice from the Extended Voice Library in your app:
Browse and preview voices using one of these options:
Use Google AI Studio or Agent Studio to obtain the voice
id.Query the Voices API
listmethod (ListVoiceswithtype=prebuiltand an optionalsearchquery) to obtain the voiceid. Learn more in the Gemini Developer API documentation or Agent Platform Gemini API (formerly Vertex AI) documentation.
Pass the library voice's
idstring as the voice name inSpeechConfig(similar to what's shown in the single-speaker example earlier on this page).
Custom voices: Voice design and voice replication
The Gemini 3.x TTS models also support custom voices using either of the following options:
Voice design (Preview): Generate a unique custom voice from a natural-language description of a speaker's age, vocal timbre, accent, and persona (without needing any reference audio). Returns a stored voice ID (
voice_...).Voice replication (Preview): Replicate a speaker's voice, tone, and cadence from a reference recording and a spoken consent recording. Returns either a project-stored voice ID (
voice_...) or a stateless, app-managed voice replication key (voicekey_...).
Use a custom voice in your Firebase AI Logic app
Creating and managing custom voices is an administrative or server-side
operation, so the Firebase AI Logic client SDKs don't directly expose the
Voices management endpoints (CreateVoice, ListVoices, DeleteVoice).
Instead, to use a custom voice, you need to do the following:
Create the custom voice once using one of these options:
Use Google AI Studio or Agent Studio to obtain your
voice_<id>orvoicekey_<id>identifier.Use the Voices API to obtain your
voice_<id>orvoicekey_<id>identifier. Learn more in your chosen Gemini API provider documentation:Gemini Developer API: Speech generation
Agent Platform Gemini API (formerly Vertex AI): Voice design and Voice replication
Pass the
voice_<id>orvoicekey_<id>string as the voice name inSpeechConfig(similar to what's shown in the single-speaker example earlier on this page).
Keep the following requirements in mind when using custom voices:
Location (Agent Platform Gemini API (formerly Vertex AI)): Custom voices (
voice_...andvoicekey_...) are only supported in thegloballocation (which is the default location when using Firebase AI Logic).Single-speaker requests:
MultiSpeakerVoiceConfigonly supports the core prebuilt voices. To use custom designed or replicated voices in a multi-character dialogue, synthesize each character's turn in a separate single-speaker request.Retention and expiration: Stored custom voices (
voice_...) expire if unused for 1 year (each synthesis request resets the 1-year retention timer); stateless voice replication keys (voicekey_...) expire 7 days after creation.
Supported languages
The Gemini 3.x TTS models automatically detect the language from your input text, including when a prompt switches languages across turns in a multi-speaker dialogue.
- Gemini 3.8 Flash TTS (
gemini-3.8-flash-tts): Supports 132 languages. - Gemini 3.8 Flash‑Lite TTS (
gemini-3.8-flash-lite-tts): Supports 103 languages.
| Language | Gemini 3.8 Flash TTS | Gemini 3.8 Flash‑Lite TTS |
|---|---|---|
| Acehnese (Arab script) | ||
| Afrikaans | ||
| Akan | ||
| Amharic | ||
| Armenian | ||
| Assamese | ||
| Awadhi | ||
| Balinese | ||
| Bangla | ||
| Banjar (Arab script) | ||
| Banjar (Latn script) | ||
| Bashkir | ||
| Basque | ||
| Belarusian | ||
| Bemba | ||
| Bhojpuri | ||
| Bosnian | ||
| Buginese | ||
| Bulgarian | ||
| Burmese | ||
| Cantonese | ||
| Catalan | ||
| Cebuano | ||
| Central Kurdish | ||
| Chhattisgarhi | ||
| Chinese (Hans script) | ||
| Chinese (Hant script) | ||
| Crimean Tatar | ||
| Croatian | ||
| Czech | ||
| Danish | ||
| Dutch | ||
| Dyula | ||
| Dzongkha | ||
| Egyptian Arabic | ||
| English | ||
| Estonian | ||
| Filipino | ||
| Finnish | ||
| French | ||
| Galician | ||
| Ganda | ||
| Georgian | ||
| German | ||
| Greek | ||
| Guarani | ||
| Gujarati | ||
| Haitian Creole | ||
| Halh Mongolian | ||
| Hausa | ||
| Hebrew | ||
| Hindi | ||
| Hungarian | ||
| Icelandic | ||
| Igbo | ||
| Iloko | ||
| Indonesian | ||
| Iranian Persian | ||
| Italian | ||
| Japanese | ||
| Javanese | ||
| Kabyle | ||
| Kamba | ||
| Kannada | ||
| Kashmiri (Arab script) | ||
| Kashmiri (Deva script) | ||
| Kazakh | ||
| Khmer | ||
| Kikuyu | ||
| Kinyarwanda | ||
| Kongo | ||
| Korean | ||
| Kyrgyz | ||
| Lao | ||
| Latgalian | ||
| Lingala | ||
| Lithuanian | ||
| Luxembourgish | ||
| Macedonian | ||
| Magahi | ||
| Maithili | ||
| Malayalam | ||
| Maltese | ||
| Manipuri | ||
| Marathi | ||
| Minangkabau (Arab script) | ||
| Minangkabau (Latn script) | ||
| Mizo | ||
| Nepali (individual language) | ||
| Nigerian Fulfulde | ||
| North Azerbaijani | ||
| Northern Sotho | ||
| Northern Uzbek | ||
| Norwegian Bokmål | ||
| Norwegian Nynorsk | ||
| Nyanja | ||
| Occitan | ||
| Odia (individual language) | ||
| Pangasinan | ||
| Persian (Afghanistan) | ||
| Polish | ||
| Portuguese | ||
| Punjabi | ||
| Romanian | ||
| Russian | ||
| Santali | ||
| Serbian | ||
| Sindhi | ||
| Sinhala | ||
| Slovak | ||
| Slovenian | ||
| Somali | ||
| South Azerbaijani | ||
| Southern Pashto | ||
| Southern Sotho | ||
| Spanish | ||
| Standard Arabic (Arab script) | ||
| Standard Arabic (Latn script) | ||
| Standard Latvian | ||
| Standard Malay | ||
| Swahili (individual language) | ||
| Swati | ||
| Swedish | ||
| Tajik | ||
| Tamil | ||
| Telugu | ||
| Thai | ||
| Tigrinya | ||
| Tosk Albanian | ||
| Turkish | ||
| Uyghur | ||
| Vietnamese |
(Optional) Explicitly set a language code
If you want to explicitly constrain speech synthesis to a specific locale, you
can pass a BCP-47 languageCode in your SpeechConfig:
The following BCP-47 language codes are provided here for convenience because they're some of the more common languages. However, you're not limited to only these languages if you want to explicitly set a language code.
- Arabic:
ar-XA - Bengali:
bn-IN - Chinese (Mandarin):
cmn-CN - Dutch:
nl-NL - English:
en-US,en-GB,en-AU,en-IN - French:
fr-FR,fr-CA - German:
de-DE - Gujarati:
gu-IN - Hindi:
hi-IN - Indonesian:
id-ID - Italian:
it-IT - Japanese:
ja-JP - Kannada:
kn-IN - Korean:
ko-KR - Malayalam:
ml-IN - Marathi:
mr-IN - Polish:
pl-PL - Portuguese:
pt-BR - Russian:
ru-RU - Spanish:
es-US,es-ES - Tamil:
ta-IN - Telugu:
te-IN - Thai:
th-TH(supported bygemini-3.8-flash-ttsonly) - Turkish:
tr-TR - Vietnamese:
vi-VN
Audio output formats
The default audio format returned in InlineDataPart depends on whether you
make a unary or streaming request:
| Request method | Default returned MIME type | Audio format details |
|---|---|---|
|
Unary ( generateContent)
|
audio/wav
|
Complete WAV file (16-bit linear PCM, 24 kHz, mono,
little-endian) with a standard 44-byte RIFF header.
Can be played directly by standard platform audio players (such as
|
|
Streaming ( generateContentStream)
|
audio/l16
|
Headerless raw 16-bit signed little-endian PCM chunks
at 24 kHz, mono.
Designed for low-latency buffer queuing and concatenation without per-chunk file headers. |
Limitations and requirements
Keep the following limitations and requirements in mind when using the Gemini 3.x TTS models:
Text-only input, audio-only output: The TTS models accept text input only (
TextPart) and requireresponseModalitiesto be set toAUDIO.Verbatim synthesis only: The models recite the provided transcript verbatim and do not generate conversational text replies.
Token limits: Maximum input is 8,192 tokens and maximum output is 16,384 tokens per request.
Multi-speaker limit:
MultiSpeakerVoiceConfigrequires 2 speakers and supports core prebuilt voices only. EveryTextPartin a multi-speaker request must include aSpeechMetadata.speakermatching one of the configured speakers.Unsupported parameters and methods:
- The Firebase AI Logic SDKs do not yet support setting
responseFormatinGenerationConfigto override the default audio output format. - Setting
systemInstruction,temperature,topP,topK,candidateCount,presencePenalty, orfrequencyPenaltyis not supported and causes requests to fail with a400 INVALID_ARGUMENTerror. - Calling
countTokensis not supported for the TTS models (token counts are returned inusageMetadataon the response).
- The Firebase AI Logic SDKs do not yet support setting
Location (Agent Platform Gemini API (formerly Vertex AI)): Custom voices (
voice_...andvoicekey_...) require thegloballocation (which is the default location when using Firebase AI Logic).
Migrate from gemini-3.1-flash-tts-preview
Both gemini-3.8-flash-tts and gemini-3.8-flash-lite-tts use the same
request structure. Review the
model comparison table to choose the best
replacement for your workload, and update your code as follows:
| Area |
gemini-3.1-flash-tts-preview (Legacy)
|
gemini-3.8-flash-tts andgemini-3.8-flash-lite-tts
|
|---|---|---|
| Delivery style & direction |
Embedded in the prompt text. For example:"Say cheerfully: ...",[Audio Profile],[Director's Notes]
|
Passed in SpeechMetadata(style: "...") on each
TextPart
Keep |
| Multi-speaker dialogue |
Single text string with inline "Speaker: ..." prefixes
|
One TextPart per dialogue turn, with
SpeechMetadata(speaker: "...", style: "...") on each
TextPart
|
| Inline vocal tags |
Square brackets. For example,[sigh] or [laughs]
|
Angle brackets. For example,<sigh> or
<laugh> or
<short pause>
Write spoken disfluencies like |
Default unary audio format
(generateContent)
|
Headerless raw 16-bit PCM (audio/pcm, 24 kHz, mono)
|
Complete WAV file (audio/wav, 16-bit PCM,
24 kHz, mono, with a 44-byte RIFF header)
Streaming
( |
| System instructions, temperature, top-P, top-K | Ignored |
Rejected with a 400 INVALID_ARGUMENT error. Remove these
parameters from your model configuration.
|
Migration checklist
Upgrade to the latest version of the Firebase AI Logic SDK and update your model name to
gemini-3.8-flash-ttsorgemini-3.8-flash-lite-tts.Move style instructions into
SpeechMetadata.style: Remove"Say cheerfully:"prefixes,[Audio Profile],[Scene], and[Director's Notes]blocks from your prompt strings so the model doesn't read them aloud. Pass a concise style description inSpeechMetadata.styleinstead.Split multi-speaker scripts into separate
TextPartturns: Replace"Joe: ... \n Jane: ..."text blocks with an array/list ofTextPartobjects where each turn setsSpeechMetadata.speaker.Convert
[...]tags to<...>tags: Replace square-bracket audio tags (like[whispers],[sighs],[laughs]) with angle-bracket vocal tags (like<whispers>,<sigh>,<laugh>,<short pause>).Update unary audio playback: Because
generateContentnow returns a complete WAV file (with a 44-byte RIFF header) instead of headerless raw PCM, remove any code that manually prepends a WAV header to unary responses (or skip the 44-byte header if feeding raw PCM samples into a low-level buffer API like Android'sAudioTrackor Unity'sAudioClip).Remove unsupported config parameters: Remove
temperature,topP,topK,candidateCount,presencePenalty,frequencyPenalty, andsystemInstructionfrom your TTSGenerativeModelconfiguration.
What else can you do?
-
Start thinking about preparing for production (see the
production checklist):
- Enforce Firebase App Check as early as possible to help protect the Gemini API from abuse by unauthorized clients.
- Use Firebase Remote Config or server prompt templates so that you can make on-demand changes to configurations for your AI feature (like model name) without releasing a new version of your app.
Try out other capabilities
- Build multi-turn conversations (chat).
- Generate text from text-only prompts.
- Generate structured output (like JSON) from both text and multimodal prompts.
- Generate and edit images from both text and multimodal prompts.
- Stream input and output (including audio) using the Gemini Live API.
-
Use tools (like function calling
and Grounding with
Google Search orGoogle Maps ) to connect a Gemini model to other parts of your app and external systems and information.
Learn how to control content generation
- Understand prompt design, including best practices, strategies, and example prompts.
- Configure model parameters like max output tokens, the probability of repeated output tokens, etc.
- Use safety settings to adjust the likelihood of getting responses that may be considered harmful.
Learn more about the supported models
Learn about the models available for various use cases and their quotas and pricing.Give feedback about your experience with Firebase AI Logic