Text to speech
Overtone turns a line of text into expressive 48 kHz speech: the feeling, the laughs and sighs, whispered or shouted words, all written into the text. Try every option on the TTS page before you write code.
curl https://tts-api.yoob.com/v1/audio/speech \
-H "Authorization: Bearer $YOOB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "yoob-tts-1", "voice": "m0770", "input": "[tender] Hey. [sigh] Whatever happens tomorrow, you are not doing it alone."}' \
--output line.wavVoices
Each voice is a designed character. Pass its id as voice. These are the voices the console offers, each with a sample on the TTS page; GET /voices lists every voice the API knows, including 414 more Pro voices with regional accents that are not in this table.
| Voice | Name | Sounds | Speaker |
|---|---|---|---|
| w1243 | Amber, late-night confidante | Breathy, intimate, hushed | Woman, mid 20s, American |
| m0770 | David, gravel-voiced storyteller | Deep, warm, gravelly | Man, early 30s, American |
| w1371 | Luna, friendly language tutor | Bright, clear, friendly | Woman, early 30s, American |
| m0868 | James, calm documentary narrator | Deep, warm, measured | Man, early 40s, American |
| w0744 | Astrid, sharp-witted designer | Crisp, bright, articulate | Woman, late 20s, British |
| m1419 | Walter, wise old radio host | Deep, smooth, resonant | Man, early 60s, American |
| w0769 | Valentina, big-hearted home cook | Smoky, bright, breathy | Woman, mid 20s, American |
| m1381 | Ryan, energetic sports fan | Youthful, bright, dynamic | Man, late 20s, American |
| w0754 | Leonie, elegant architect | Velvety, smooth, refined | Woman, early 40s, British |
| m0042 | Marcus, seasoned detective | Deep, gravelly, resonant | Man, mid 40s, American · pro voice |
| w0445 | Zoe, sweet best friend | Sweet, warm, youthful | Woman, mid 20s, American |
| m1573 | Tyler, upbeat podcast host | Bright, punchy, clear | Man, late 20s, American |
| m0290 | Adam, soft-spoken romantic | Deep, warm, intimate | Man, mid 20s, American |
| w0250 | Linda, warm British teacher | Warm, refined, melodious | Woman, late 50s, British |
| m0886 | Leo, laid-back musician | Husky, soft, warm | Man, mid 20s, American |
| w0964 | Eleanor, velvet-voiced stage actress | Velvety, husky, breathy | Woman, late 50s, British |
| m0112 | Jake, easygoing surfer | Warm, youthful, raspy | Man, mid 20s, American |
| w1438 | Chloe, bubbly California creator | Bright, youthful, airy | Woman, early 20s, Californian |
| m0602 | Ben, dry-humored barista | Warm, mellow, dry | Man, mid 20s, American |
| w0110 | Rachel, no-nonsense news anchor | Crisp, assertive, dry | Woman, early 30s, American |
| m0035 | Sam, curious student | Bright, youthful, clear | Man, young adult, American |
| w0073 | Scarlett, smoky jazz singer | Sultry, velvety, breathy | Woman, late 20s, American |
| w1058 | Emma, cheerful morning host | Bright, crisp, youthful | Woman, early 30s, American |
| w0059 | Mia, soothing meditation guide | Soft, breathy, sensual | Woman, late 20s, American |
| w0043 | Lily, dreamy poet | Airy, breathy, youthful | Woman, early 20s, American |
| h_aus_w | Matilda, sunny Aussie adventurer | Bright, playful, energetic | Woman, late 20s, Australian · pro voice |
| h_irish_m | Liam, charming pub storyteller | Warm, lively, charming | Man, early 30s, Irish · pro voice |
| h_scot_m | Callum, rugged Highland guide | Rich, rugged, warm | Man, mid 40s, Scottish · pro voice |
| h_ind_w | Priya, warm Mumbai teacher | Warm, melodic, articulate | Woman, early 30s, Indian · pro voice |
| h_south_m | Wyatt, southern ranch owner | Slow, smooth, drawling | Man, late 40s, Southern US · pro voice |
| h_brit_m | Oliver, witty London barrister | Crisp, dry, witty | Man, mid 30s, British · pro voice |
| h_ny_w | Nina, sassy New York cabbie | Raspy, sassy, confident | Woman, early 40s, New York · pro voice |
| h_fr_w | Camille, parisian art curator | Soft, elegant, breathy | Woman, late 20s, French · pro voice |
| h_trailer_m | Victor, movie trailer voice | Booming, cinematic, deep | Man, mid 50s, American · pro voice |
Emotion, sound and delivery tags
Write how the line should sound into the line itself, in lower-case square brackets.
Emotion, at the start of the line
One tag first sets the feeling of the whole line, and the voice's pacing with it: sad, tender and intimate lines are slower. Use the exact names below; common synonyms such as happy or scared are mapped onto them.
[angry][calm][desire][disgust][excited][fear][flirty][grief][hurt][intimate][joy][longing][nervous][neutral][playful][relief][sad][shock][surprise][tender][tired]Sounds, anywhere
A sound is performed where it stands. One in the middle of a line starts a new chunk there.
[laughing][giggle][chuckle][sigh][gasp][breathing][inhale][exhale][crying][sobbing][sniff][moaning][groan][kissing][panting][cough][clear throat][yawn][scream]Delivery spans, around words
Wrap words to say them a particular way, and close the span: an unclosed span is closed for you, but closed spans render better. [pause] is dropped.
[whisper] … [/whisper][low voice] … [/low voice][shouting] … [/shouting][tearful] … [/tearful][trembling] … [/trembling]All three together:
[sad] [sigh] I walked past his old office today. [tearful] The nameplate was already gone. [/tearful]Quality
quality trades the first sound for polish. Long inputs are split into chunks of up to about 40 words, at sentence ends and before mid-line sounds, and every chunk keeps the line's leading emotion.
| quality | What happens | Use it for |
|---|---|---|
best | 8 takes of each chunk; a word check drops takes that slur or skip words, and the cleanest, most in-voice take is kept. With 2 or more chunks the first is a single quick take, so audio starts at once. The default. | Anything listened to |
best_all | Best of 8 on every chunk, the first included. | Short lines where every word matters |
fast | One take per chunk, no scoring. | Drafts, previews, the first reply in a conversation |
"pick": "expressive" ranks best takes of an emotion-tagged chunk by how clearly they carry the feeling instead of by overall polish: more emotional, a little less clean.
Output formats
All audio is 48 kHz mono. Choose the container with response_format:
| response_format | What you get |
|---|---|
wav | WAV, 16-bit. The default. |
pcm | Raw signed 16-bit little-endian samples, no header. Best for streaming into a player. |
mp3 | MP3 |
opus | Opus in Ogg |
aac | AAC (ADTS) |
flac | FLAC |
Streaming audio
With "stream": true the response arrives chunk by chunk as each chunk is picked, so playback can start while the rest renders. With pcm, pipe it straight into a player:
curl -N https://tts-api.yoob.com/v1/audio/speech \
-H "Authorization: Bearer $YOOB_API_KEY" -H "Content-Type: application/json" \
-d '{"model": "yoob-tts-1", "voice": "w1243", "input": "[excited] Okay, okay, do not freak out. [gasp] They want me to start on Monday!", "response_format": "pcm", "stream": true}' \
| ffplay -f s16le -ar 48000 -ac 1 -nodisp -autoexit -Response headers describe the render: x-yoob-seed (pass it back as seed to reproduce the audio), x-yoob-quality, x-yoob-sample-rate and x-request-id.
Streaming text in
When the text itself is still being written, by a language model for example, open the speech WebSocket and send the text as it arrives. Each sentence is voiced as soon as it is complete, all in one render that keeps one voice and one seed. It is one connection per reply, and the rate limit counts connections, not sentences.
const ws = new WebSocket("wss://tts-api.yoob.com/v1/audio/speech/stream", {
headers: { authorization: `Bearer ${process.env.YOOB_API_KEY}` }, // Node; see Authentication for browsers
});
ws.binaryType = "arraybuffer";
ws.onopen = async () => {
ws.send(JSON.stringify({ voice: "w1243", quality: "best", response_format: "pcm" }));
for await (const delta of llmReply) ws.send(JSON.stringify({ text: delta }));
ws.send(JSON.stringify({ end: true }));
};
ws.onmessage = (e) => {
if (typeof e.data !== "string") return play(e.data); // 48 kHz s16le audio
const m = JSON.parse(e.data); // ready, chunk, done or error
};- Text is cut after
. ! ? …once the next character arrives, never inside a tag, an open span or an abbreviation such as “Dr.”. - A later sentence shorter than
min_words(3) waits for the next one; the first sentence always goes at once. - Text that runs past 40 words without a sentence end is cut at its last comma, semicolon or colon.
{"flush": true}voices whatever is buffered now. 30 s with no message counts as the end; at most 20,000 characters per connection.
Cancelling
Closing the connection cancels a render. When something sits between you and the API that may not pass the close on quickly, give the request your own id and cancel it explicitly, which stops it at once:
# when speaking
-d '{"model": "yoob-tts-1", "voice": "w1243", "input": "...", "request_id": "reply-42"}'
# to stop it
curl https://tts-api.yoob.com/v1/audio/speech/cancel \
-H "Authorization: Bearer $YOOB_API_KEY" -H "Content-Type: application/json" \
-d '{"request_id": "reply-42"}'Ids are 1–64 characters of [A-Za-z0-9._-], unique per key while running. An id cancelled before its request arrives is remembered for 30 s, and that request is refused.
Speed, seed and delivery
| Field | What it does |
|---|---|
speed | 0.5 to 2.0. The voice speaks the same words in 1/speed of the time; it is not a time-stretch. |
seed | An integer, or a string of 1–64 characters. The same input, voice, quality, speed, instructions and seed always give the same audio. |
instructions | Free-text delivery, up to 300 characters. Short comma lists work best: "gentle voice, warm voice, comforting, softly spoken". |
input | Up to 4,096 characters per request. |