> ## Documentation Index
> Fetch the complete documentation index at: https://docs.camb.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# TTS with SDK

> Generate speech from text using the Python or TypeScript SDK

export const AudioPlayer = ({src, title, prompt, badge, resetOnPause = true, exclusive = true}) => {
  const audioRef = React.useRef(null);
  const [playing, setPlaying] = React.useState(false);
  const [progress, setProgress] = React.useState(0);
  const [hovering, setHovering] = React.useState(false);
  const uid = React.useMemo(() => 'p' + Math.random().toString(36).slice(2, 8), []);
  React.useEffect(() => {
    const audio = audioRef.current;
    if (!audio) return;
    const onTime = () => setProgress(audio.duration ? audio.currentTime / audio.duration : 0);
    const onEnd = () => {
      setPlaying(false);
      setProgress(0);
    };
    audio.addEventListener('timeupdate', onTime);
    audio.addEventListener('ended', onEnd);
    const onGlobalStop = e => {
      if (e.detail !== uid && !audio.paused) {
        audio.pause();
        audio.currentTime = 0;
        setPlaying(false);
        setProgress(0);
      }
    };
    if (exclusive) window.addEventListener('audio-player-stop', onGlobalStop);
    return () => {
      audio.removeEventListener('timeupdate', onTime);
      audio.removeEventListener('ended', onEnd);
      if (exclusive) window.removeEventListener('audio-player-stop', onGlobalStop);
    };
  }, []);
  const toggle = e => {
    if (e) e.stopPropagation();
    const audio = audioRef.current;
    if (!audio) return;
    if (audio.paused) {
      if (exclusive) window.dispatchEvent(new CustomEvent('audio-player-stop', {
        detail: uid
      }));
      audio.currentTime = 0;
      setProgress(0);
      audio.play().catch(() => setPlaying(false));
      setPlaying(true);
    } else {
      audio.pause();
      if (resetOnPause) {
        audio.currentTime = 0;
        setProgress(0);
      }
      setPlaying(false);
    }
  };
  const circleSize = 52;
  const strokeWidth = 3;
  const radius = (circleSize - strokeWidth) / 2;
  const circumference = 2 * Math.PI * radius;
  const strokeDashoffset = circumference * (1 - progress);
  return <div style={{
    borderRadius: '14px',
    border: '1px solid rgba(236,85,18,0.12)',
    background: 'linear-gradient(160deg, rgba(236,85,18,0.04) 0%, transparent 40%, rgba(236,85,18,0.02) 100%)',
    overflow: 'hidden',
    marginBottom: '16px'
  }}>
      <div style={{
    height: '2px',
    background: 'linear-gradient(90deg, transparent, #EC5512 30%, #FF8A5C 50%, #EC5512 70%, transparent)'
  }} />
      <div style={{
    padding: '18px 22px 16px',
    display: 'flex',
    alignItems: 'center',
    gap: '16px'
  }}>
        <div style={{
    flexShrink: 0,
    position: 'relative',
    width: '52px',
    height: '52px',
    cursor: 'pointer'
  }} onMouseEnter={() => setHovering(true)} onMouseLeave={() => setHovering(false)}>
          <svg width={circleSize} height={circleSize} style={{
    position: 'absolute',
    top: 0,
    left: 0,
    transform: 'rotate(-90deg)'
  }}>
            <circle cx={26} cy={26} r={radius} fill="none" stroke="rgba(255,255,255,0.08)" strokeWidth={strokeWidth} />
            <circle cx={26} cy={26} r={radius} fill="none" stroke={`url(#${uid})`} strokeWidth={strokeWidth} strokeDasharray={circumference} strokeDashoffset={strokeDashoffset} strokeLinecap="round" style={{
    transition: 'stroke-dashoffset 0.15s ease'
  }} />
            <defs><linearGradient id={uid} x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" stopColor="#EC5512" /><stop offset="100%" stopColor="#FF8A5C" /></linearGradient></defs>
          </svg>
          <button onClick={toggle} style={{
    position: 'absolute',
    top: '50%',
    left: '50%',
    transform: 'translate(-50%, -50%)',
    width: '40px',
    height: '40px',
    borderRadius: '50%',
    background: hovering ? 'linear-gradient(145deg, #F06020, #EC5512)' : 'linear-gradient(145deg, #EC5512, #D44A0F)',
    border: 'none',
    cursor: 'pointer',
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    boxShadow: hovering ? '0 2px 12px rgba(236,85,18,0.45)' : '0 1px 4px rgba(236,85,18,0.25)',
    transition: 'all 0.2s ease',
    padding: playing ? '0' : '0 0 0 1.5px'
  }}>
            {playing ? <svg width="14" height="14" viewBox="0 0 24 24" fill="white" stroke="none"><rect x="6" y="4" width="4" height="16" rx="1" /><rect x="14" y="4" width="4" height="16" rx="1" /></svg> : <svg width="14" height="14" viewBox="0 0 24 24" fill="white" stroke="none"><polygon points="7 3 21 12 7 21" /></svg>}
          </button>
        </div>
        <div style={{
    flex: 1,
    minWidth: 0
  }}>
          <div style={{
    display: 'flex',
    alignItems: 'center',
    gap: '8px',
    flexWrap: 'wrap',
    marginBottom: prompt ? '2px' : '4px'
  }}>
            <span style={{
    fontWeight: 600,
    fontSize: '14px',
    letterSpacing: '-0.01em'
  }}>{title}</span>
            {badge && <span style={{
    fontSize: '10px',
    fontWeight: 600,
    padding: '2px 7px',
    borderRadius: '4px',
    background: 'rgba(236,85,18,0.08)',
    color: '#EC5512',
    fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',
    letterSpacing: '0.02em',
    border: '1px solid rgba(236,85,18,0.12)',
    whiteSpace: 'nowrap'
  }}>{badge}</span>}
          </div>
          {prompt && <div style={{
    fontSize: '12.5px',
    opacity: 0.5,
    lineHeight: '1.4',
    fontStyle: 'italic'
  }}>"{prompt}"</div>}
        </div>
      </div>
      <audio ref={audioRef} preload="metadata" src={src} />
    </div>;
};

## Overview

Generate natural-sounding speech from text using the CAMB.AI SDK. This tutorial covers installation, generating your first audio, choosing a model, and listing voices.

### Listen to an Example

<AudioPlayer src="/audio/demo-accent-en-us.wav" title="SDK Output" prompt="Welcome to our service. We're glad to have you here." badge="en-us" />

### Prerequisites

<Steps>
  <Step title="Create an account">
    Sign up at [CAMB.AI Studio](https://studio.camb.ai) if you haven't already.
  </Step>

  <Step title="Get your API key">
    Go to **Settings → API Keys** in Studio and copy your key. See [Authentication](/getting-started/authentication) for details.
  </Step>

  <Step title="Install the SDK">
    <CodeGroup>
      ```bash Python theme={null}
      pip install camb-sdk
      ```

      ```bash TypeScript theme={null}
      npm install @camb-ai/sdk
      ```
    </CodeGroup>

    Skip this step if you're using the [direct API](/tutorials/direct-api).
  </Step>

  <Step title="Set your API key to use in your code">
    ```bash theme={null}
    export CAMB_API_KEY="your_api_key_here"
    ```
  </Step>
</Steps>

***

## Code

<CodeGroup>
  ```python Python theme={null}
  import os
  from camb.client import CambAI, save_stream_to_file
  from camb.types import StreamTtsOutputConfiguration

  client = CambAI(api_key=os.getenv("CAMB_API_KEY"))

  # Generate speech and save to file
  response = client.text_to_speech.tts(
      text="[excited] We just shipped the feature, and the response has been fantastic!",
      voice_id=147320,
      language="en-us",
      speech_model="mars-8.1-flash-beta",
      output_configuration=StreamTtsOutputConfiguration(format="wav")
  )

  save_stream_to_file(response, "output.wav")
  print("Audio saved to output.wav")
  ```

  ```javascript TypeScript theme={null}
  import { CambClient, saveStreamToFile } from '@camb-ai/sdk';

  const client = new CambClient({
      apiKey: process.env.CAMB_API_KEY
  });

  async function main() {
      // Generate speech and save to file
      const response = await client.textToSpeech.tts({
          text: '[excited] We just shipped the feature, and the response has been fantastic!',
          voice_id: 147320,
          language: 'en-us',
          speech_model: 'mars-8.1-flash-beta',
          output_configuration: { format: 'wav' }
      });

      await saveStreamToFile(response, 'output.wav');
      console.log('Audio saved to output.wav');
  }

  main();
  ```
</CodeGroup>

***

## Listing Voices

List available voices to find the right one for your use case:

<CodeGroup>
  ```python Python theme={null}
  voices = client.voice_cloning.list_voices()

  for voice in voices[:10]:
      print(f"ID: {voice['id']}, Name: {voice['voice_name']}, Gender: {voice['gender']}")
  ```

  ```javascript TypeScript theme={null}
  const voices = await client.voiceCloning.listVoices();

  for (const voice of voices.slice(0, 10)) {
      console.log(`ID: ${voice.id}, Name: ${voice.voice_name}, Gender: ${voice.gender}`);
  }
  ```
</CodeGroup>

***

## Parameters

### Required

| Parameter  | Type    | Description                                  |
| ---------- | ------- | -------------------------------------------- |
| `text`     | string  | Text to convert to speech (min 3 characters) |
| `voice_id` | integer | Voice ID to use (e.g., `147320`)             |

### Optional

| Parameter              | Type   | Default        | Description                                                                              |
| ---------------------- | ------ | -------------- | ---------------------------------------------------------------------------------------- |
| `language`             | string | `"en-us"`      | BCP-47 language code                                                                     |
| `speech_model`         | string | `"mars-flash"` | `mars-8.1-flash-beta`, `mars-8.1-pro-beta`, `mars-flash`, `mars-pro`, or `mars-instruct` |
| `output_configuration` | object | `{}`           | Output format: `wav`, `mp3`, or `pcm_s16le`                                              |

***

`mars-instruct` supports embedded emotion tags and SSML-style breaks for fine-grained control. See [Emotional Voice Control](/tutorials/emotional-voice-control) for examples.

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Emotional Voice Control" icon="sparkles" href="/tutorials/emotional-voice-control">
    Add emotional expression and dramatic pacing with mars-instruct.
  </Card>

  <Card title="TTS with Accents" icon="globe" href="/tutorials/tts-with-accents">
    Generate speech in 140+ language accents with the same voice.
  </Card>

  <Card title="Voice Cloning" icon="mic" href="/tutorials/voice-cloning">
    Clone a voice from reference audio and generate speech with it.
  </Card>

  <Card title="MARS 8 Models" icon="bolt" href="/choosing-a-model">
    Compare model variants in detail.
  </Card>
</CardGroup>
