> ## 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.

# Text to Sound Effects

> Generate sound effects and music from text prompts

export const UseCaseCard = ({src, title, description, prompt, badge, icon, href, 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(() => 'u' + Math.random().toString(36).slice(2, 8), []);
  const hasAudio = !!src;
  React.useEffect(() => {
    const audio = audioRef.current;
    if (!audio || !hasAudio) 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);
    };
  }, [hasAudio, exclusive, uid]);
  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 = 32;
  const strokeWidth = 2;
  const radius = (circleSize - strokeWidth) / 2;
  const circumference = 2 * Math.PI * radius;
  const strokeDashoffset = circumference * (1 - progress);
  return <div onClick={() => href && (window.location.href = href)} style={{
    borderRadius: '12px',
    border: '1px solid rgba(255,255,255,0.08)',
    background: 'rgba(255,255,255,0.02)',
    padding: '20px',
    display: 'flex',
    flexDirection: 'column',
    gap: '12px',
    cursor: href ? 'pointer' : 'default',
    transition: 'border-color 0.2s ease, background 0.2s ease'
  }} onMouseEnter={e => {
    if (href) {
      e.currentTarget.style.borderColor = 'rgba(236,85,18,0.25)';
      e.currentTarget.style.background = 'rgba(236,85,18,0.03)';
    }
  }} onMouseLeave={e => {
    if (href) {
      e.currentTarget.style.borderColor = 'rgba(255,255,255,0.08)';
      e.currentTarget.style.background = 'rgba(255,255,255,0.02)';
    }
  }}>
      <div style={{
    display: 'flex',
    alignItems: 'flex-start',
    justifyContent: 'space-between',
    gap: '12px'
  }}>
        <div style={{
    flex: 1,
    minWidth: 0
  }}>
          <div style={{
    display: 'flex',
    alignItems: 'center',
    gap: '8px',
    marginBottom: '8px'
  }}>
            {icon && <span style={{
    fontSize: '18px'
  }}>{icon}</span>}
            <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',
    border: '1px solid rgba(236,85,18,0.12)',
    whiteSpace: 'nowrap'
  }}>{badge}</span>}
            {href && <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{
    opacity: 0.3,
    marginLeft: 'auto',
    flexShrink: 0
  }}><path d="M7 17L17 7" /><path d="M7 7h10v10" /></svg>}
          </div>
          {description && <div style={{
    fontSize: '13px',
    opacity: 0.55,
    lineHeight: '1.5',
    marginBottom: prompt ? '6px' : '0'
  }}>{description}</div>}
          {prompt && <div style={{
    fontSize: '12px',
    opacity: 0.4,
    lineHeight: '1.4',
    fontStyle: 'italic'
  }}>"{prompt}"</div>}
        </div>
        {hasAudio && <div style={{
    flexShrink: 0,
    position: 'relative',
    width: '32px',
    height: '32px',
    cursor: 'pointer',
    marginTop: '2px'
  }} onMouseEnter={() => setHovering(true)} onMouseLeave={() => setHovering(false)}>
            <svg width={circleSize} height={circleSize} style={{
    position: 'absolute',
    top: 0,
    left: 0,
    transform: 'rotate(-90deg)'
  }}>
              <circle cx={16} cy={16} r={radius} fill="none" stroke="rgba(255,255,255,0.08)" strokeWidth={strokeWidth} />
              <circle cx={16} cy={16} 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: '24px',
    height: '24px',
    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 10px rgba(236,85,18,0.4)' : '0 1px 4px rgba(236,85,18,0.2)',
    transition: 'all 0.2s ease',
    padding: playing ? '0' : '0 0 0 1px'
  }}>
              {playing ? <svg width="9" height="9" 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="9" height="9" viewBox="0 0 24 24" fill="white" stroke="none"><polygon points="7 3 21 12 7 21" /></svg>}
            </button>
          </div>}
      </div>
      {hasAudio && <audio ref={audioRef} preload="none" src={src} />}
    </div>;
};

export const AudioGridItem = ({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 [promptHover, setPromptHover] = React.useState(false);
  const uid = React.useMemo(() => 'g' + 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 = 44;
  const strokeWidth = 2.5;
  const radius = (circleSize - strokeWidth) / 2;
  const circumference = 2 * Math.PI * radius;
  const strokeDashoffset = circumference * (1 - progress);
  return <div style={{
    borderRadius: '12px',
    border: '1px solid rgba(236,85,18,0.10)',
    background: 'linear-gradient(160deg, rgba(236,85,18,0.03) 0%, transparent 50%)',
    overflow: 'hidden'
  }}>
      <div style={{
    height: '2px',
    background: 'linear-gradient(90deg, transparent, #EC5512, transparent)',
    opacity: 0.5
  }} />
      <div style={{
    padding: '14px',
    display: 'flex',
    alignItems: 'center',
    gap: '12px'
  }}>
        <div style={{
    flexShrink: 0,
    position: 'relative',
    width: '44px',
    height: '44px',
    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={22} cy={22} r={radius} fill="none" stroke="rgba(255,255,255,0.08)" strokeWidth={strokeWidth} />
            <circle cx={22} cy={22} 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: '34px',
    height: '34px',
    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 10px 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="11" height="11" 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="11" height="11" 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: '6px',
    flexWrap: 'wrap'
  }}>
            <span style={{
    fontWeight: 600,
    fontSize: '13px',
    letterSpacing: '-0.01em',
    lineHeight: '1.2'
  }}>{title}</span>
            {badge && <span style={{
    fontSize: '10px',
    fontWeight: 600,
    padding: '1px 6px',
    borderRadius: '3px',
    background: 'rgba(236,85,18,0.08)',
    color: '#EC5512',
    fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',
    border: '1px solid rgba(236,85,18,0.10)',
    whiteSpace: 'nowrap'
  }}>{badge}</span>}
          </div>
          {prompt && <div onMouseEnter={() => setPromptHover(true)} onMouseLeave={() => setPromptHover(false)} style={{
    position: 'relative',
    fontSize: '11.5px',
    fontStyle: 'italic',
    opacity: 0.4,
    marginTop: '2px',
    lineHeight: '1.35',
    whiteSpace: 'nowrap',
    overflow: 'hidden',
    textOverflow: 'ellipsis',
    cursor: 'default'
  }}>
              "{prompt}"
              {promptHover && <div style={{
    position: 'absolute',
    bottom: 'calc(100% + 6px)',
    left: 0,
    right: 0,
    background: 'rgba(20,20,20,0.95)',
    border: '1px solid rgba(236,85,18,0.2)',
    borderRadius: '6px',
    padding: '6px 8px',
    fontSize: '11px',
    fontStyle: 'italic',
    opacity: 1,
    color: 'rgba(255,255,255,0.75)',
    whiteSpace: 'normal',
    lineHeight: '1.4',
    zIndex: 10,
    boxShadow: '0 4px 12px rgba(0,0,0,0.4)'
  }}>"{prompt}"</div>}
            </div>}
        </div>
      </div>
      <audio ref={audioRef} preload="none" src={src} />
    </div>;
};

export const AudioGrid = ({children, cols = 2}) => <div style={{
  display: 'grid',
  gridTemplateColumns: `repeat(${cols}, 1fr)`,
  gap: '10px',
  marginBottom: '16px'
}}>{children}</div>;

## Overview

Generate royalty-free sound effects or music from natural language descriptions. Describe what you want to hear and get audio output.

### Listen to Examples

<AudioGrid cols={2}>
  <AudioGridItem src="/audio/demo-sfx-laser.wav" title="Sci-Fi Laser" prompt="A futuristic sci-fi laser sound effect" badge="sound" />

  <AudioGridItem src="/audio/demo-sfx-rain.wav" title="Rain" prompt="Rain falling on a tin roof" badge="sound" />

  <AudioGridItem src="/audio/demo-sfx-jazz.wav" title="Jazz" prompt="Upbeat jazz elevator music" badge="music" />

  <AudioGridItem src="/audio/demo-sfx-thunder.wav" title="Thunder" prompt="Thunder and lightning storm" badge="sound" />

  <AudioGridItem src="/audio/demo-sfx-footsteps.wav" title="Footsteps" prompt="Footsteps on gravel" badge="sound" />
</AudioGrid>

### 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
  import time
  from camb.client import CambAI, save_stream_to_file

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

  def generate_sound():
      # Create sound effect from text prompt
      # audio_type: "sound" for effects, "music" for music
      response = client.text_to_audio.create_text_to_audio(
          prompt="Sword clashing against a metal shield in a medieval battle",
          duration=3.0,
          audio_type="sound"
      )

      task_id = response.task_id
      print(f"Task created: {task_id}")

      # Poll for completion
      while True:
          status = client.text_to_audio.get_text_to_audio_status(task_id=task_id)
          print(f"Status: {status.status}")

          if status.status == "SUCCESS":
              result = client.text_to_audio.get_text_to_audio_result(status.run_id)
              save_stream_to_file(result, "sound_effect.wav")
              print("Saved to sound_effect.wav")
              break
          elif status.status == "FAILED":
              print("Task failed!")
              break

          time.sleep(2)

  generate_sound()
  ```

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

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

  async function generateSound() {
      // Create sound effect from text prompt
      // audio_type: "sound" for effects, "music" for music
      console.log('Creating sound effect...');
      const response = await client.textToAudio.createTextToAudio({
          prompt: 'Sword clashing against a metal shield in a medieval battle',
          duration: 3.0,
          audio_type: 'sound'
      });

      const taskId = response.task_id;
      console.log(`Task created: ${taskId}`);

      // Poll for completion
      while (true) {
          const status = await client.textToAudio.getTextToAudioStatus({
              task_id: taskId
          });

          console.log(`Status: ${status.status}`);

          if (status.status === 'SUCCESS') {
              const result = await client.textToAudio.getTextToAudioResult({
                  run_id: status.run_id
              });
              await saveStreamToFile(result, 'sound_effect.wav');
              console.log('Saved to sound_effect.wav');
              break;
          } else if (status.status === 'FAILED') {
              console.log('Task failed!');
              break;
          }

          await new Promise(r => setTimeout(r, 2000));
      }
  }

  generateSound();
  ```
</CodeGroup>

***

## Parameters

| Parameter    | Description                                   | Values                 |
| ------------ | --------------------------------------------- | ---------------------- |
| `prompt`     | Natural language description of desired audio | Any text               |
| `duration`   | Length of audio in seconds                    | `1.0` - `30.0`         |
| `audio_type` | Type of audio to generate                     | `"sound"` or `"music"` |

***

## Use Cases

<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '10px', marginBottom: '16px' }}>
  <UseCaseCard src="/audio/demo-usecase-game.wav" title="Game Development" description="Generate unique sound effects for games" prompt="Sword clashing against a metal shield in a medieval battle" icon="🎮" badge="sound" />

  <UseCaseCard src="/audio/demo-usecase-video.wav" title="Video Production" description="Create custom audio for videos" prompt="Busy city intersection with car horns and pedestrian chatter" icon="🎬" badge="sound" />

  <UseCaseCard src="/audio/demo-usecase-podcast.wav" title="Podcasts" description="Add background music or transitions" prompt="Warm lo-fi hip hop beat with vinyl crackle" icon="🎙️" badge="music" />

  <UseCaseCard src="/audio/demo-usecase-prototype.wav" title="Prototyping" description="Quick audio assets for demos" prompt="Soft positive notification chime" icon="⚡" badge="sound" />
</div>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Text to Speech" icon="audio-lines" href="/tutorials/tts-with-sdk">
    Generate speech from text using the SDK.
  </Card>

  <Card title="TTS with Accents" icon="globe" href="/tutorials/tts-with-accents">
    Speak 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.
  </Card>

  <Card title="API Reference" icon="book" href="/api-reference/endpoint/create-text-to-sound">
    Full Text-to-Sound API specification.
  </Card>
</CardGroup>
