Text-to-speech generation (TTS) using the Gemini API


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-tts
  • gemini-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:

  1. In your GenerationConfig, set responseModalities to AUDIO and configure a SpeechConfig with the following:

  2. Create a TextPart with the following:

    • (Required) The verbatim transcript to be spoken.

    • (Optional) A SpeechMetadata with a style description (for example, "cheerful and friendly").

  3. 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 a Publisher 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:

  1. Create a MultiSpeakerVoiceConfig that maps two speaker names (for example, "Joe" and "Jane") to their SpeakerVoiceConfig voices.

  2. In your GenerationConfig, set responseModalities to AUDIO and pass your MultiSpeakerVoiceConfig (and optional languageCode) into SpeechConfig.

  3. Provide an array or list of TextPart objects, specifically one TextPart per dialogue turn. On every TextPart, set SpeechMetadata with the following:

    • (Required) A speaker that matches one of the speaker names defined in your MultiSpeakerVoiceConfig.
    • (Optional) A style description (for example, "cheerful and friendly").
  4. 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 a Publisher 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 a Publisher 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 style in SpeechMetadata to 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 separate TextPart turns, each with its own style.

  • 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."
  • 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!"

Best practices for consistency

  • Test without style first, then add concise style tweaks: Most requests sound natural with an empty style field. Add a short style phrase (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 using SpeechMetadata.style to 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).

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:

  1. Browse and preview voices using one of these options:

  2. Pass the library voice's id string as the voice name in SpeechConfig (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:

  1. Create the custom voice once using one of these options:

  2. Pass the voice_<id> or voicekey_<id> string as the voice name in SpeechConfig (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_... and voicekey_...) are only supported in the global location (which is the default location when using Firebase AI Logic).

  • Single-speaker requests: MultiSpeakerVoiceConfig only 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.

(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:



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 AVAudioPlayer on Apple platforms, Web Audio decodeAudioData, or file-based players).

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 require responseModalities to be set to AUDIO.

  • 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: MultiSpeakerVoiceConfig requires 2 speakers and supports core prebuilt voices only. Every TextPart in a multi-speaker request must include a SpeechMetadata.speaker matching one of the configured speakers.

  • Unsupported parameters and methods:

    • The Firebase AI Logic SDKs do not yet support setting responseFormat in GenerationConfig to override the default audio output format.
    • Setting systemInstruction, temperature, topP, topK, candidateCount, presencePenalty, or frequencyPenalty is not supported and causes requests to fail with a 400 INVALID_ARGUMENT error.
    • Calling countTokens is not supported for the TTS models (token counts are returned in usageMetadata on the response).
  • Location (Agent Platform Gemini API (formerly Vertex AI)): Custom voices (voice_... and voicekey_...) require the global location (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 and
gemini-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 TextPart text strictly to the verbatim spoken transcript.

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 "uhm" directly in the text.

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 (generateContentStream) remains headerless raw 16-bit PCM (audio/l16).

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-tts or gemini-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 in SpeechMetadata.style instead.

  • Split multi-speaker scripts into separate TextPart turns: Replace "Joe: ... \n Jane: ..." text blocks with an array/list of TextPart objects where each turn sets SpeechMetadata.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 generateContent now 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's AudioTrack or Unity's AudioClip).

  • Remove unsupported config parameters: Remove temperature, topP, topK, candidateCount, presencePenalty, frequencyPenalty, and systemInstruction from your TTS GenerativeModel configuration.



What else can you do?

Try out other capabilities

Learn how to control content generation

You can also experiment with prompts and model configurations and even get a generated code snippet using Google AI Studio.

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