# Temperature and pause tags on Paxa TTS Flash v2

Paxa TTS Flash v2 now takes a temperature that keeps a reading steadier or lets it vary more, and pause tags that put a set silence after any word.

Published 2026-10-28. Paxa Labs.

The same line in the same voice used to come back paced a little differently on every request. A narrator can use that variety. A phone menu, an app prompt that plays thousands of times, or a line rendered again to match an edit needs a read close to the one the team approved.

Two controls arrive on Paxa TTS Flash v2 today. Temperature sets how much a reading varies from one request to the next. Pause tags put a set length of silence where you write them. Both work in the console and on the API.

One line read by Foi Thong, twice at 0, 0.5, and 2:

- Temperature 0, take 1, [Foi Thong](https://paxalabs.com/voices/foithong), Narrator: https://paxalabs.com/audio/directions/temperature/foithong-t0-s1-0-1.m4a
- Temperature 0, take 2, [Foi Thong](https://paxalabs.com/voices/foithong), Narrator: https://paxalabs.com/audio/directions/temperature/foithong-t0-s2-0-2.m4a
- Temperature 0.5, take 1, [Foi Thong](https://paxalabs.com/voices/foithong), Narrator: https://paxalabs.com/audio/directions/temperature/foithong-default-s1-0.5-1.m4a
- Temperature 0.5, take 2, [Foi Thong](https://paxalabs.com/voices/foithong), Narrator: https://paxalabs.com/audio/directions/temperature/foithong-default-s2-0.5-2.m4a
- Temperature 2, take 1, [Foi Thong](https://paxalabs.com/voices/foithong), Narrator: https://paxalabs.com/audio/directions/temperature/foithong-t2-s1-2-1.m4a
- Temperature 2, take 2, [Foi Thong](https://paxalabs.com/voices/foithong), Narrator: https://paxalabs.com/audio/directions/temperature/foithong-t2-s2-2-2.m4a

## Temperature

Temperature runs from 0 to 2 in steps of 0.1, and 0.5 is the default. It changes the pacing, pauses, breaths, and melody of a reading from one request to the next. A request that leaves it out reads at 0.5, which varies less than requests did before.

- At `0`, a reading varies least. The same text in the same voice comes back closest in timing and melody from one request to the next, and two takes can still differ.
- At `0.5`, the default, a reading varies a little between requests. Leaving the field out is the same request as sending `0.5`.
- At `2`, a reading varies the most.

### Temperature with a direction

A direction sets the feeling, and temperature sets how much that reading varies. Kaprao reads a line under `[sad]` and another under `[angry]`, each twice at 0 and twice at 2.

Two lines read by Kaprao under [sad] and [angry], each twice at 0 and 2:

- [sad] at temperature 0, take 1, [Kaprao](https://paxalabs.com/voices/kaprao), Emotion tags: https://paxalabs.com/audio/directions/temperature/kaprao-sad-t0-s1-0-1.m4a
- [sad] at temperature 0, take 2, [Kaprao](https://paxalabs.com/voices/kaprao), Emotion tags: https://paxalabs.com/audio/directions/temperature/kaprao-sad-t0-s2-0-2.m4a
- [sad] at temperature 2, take 1, [Kaprao](https://paxalabs.com/voices/kaprao), Emotion tags: https://paxalabs.com/audio/directions/temperature/kaprao-sad-t2-s1-2-1.m4a
- [sad] at temperature 2, take 2, [Kaprao](https://paxalabs.com/voices/kaprao), Emotion tags: https://paxalabs.com/audio/directions/temperature/kaprao-sad-t2-s2-2-2.m4a
- [angry] at temperature 0, take 1, [Kaprao](https://paxalabs.com/voices/kaprao), Emotion tags: https://paxalabs.com/audio/directions/temperature/kaprao-angry-t0-s1-0-1.m4a
- [angry] at temperature 0, take 2, [Kaprao](https://paxalabs.com/voices/kaprao), Emotion tags: https://paxalabs.com/audio/directions/temperature/kaprao-angry-t0-s2-0-2.m4a
- [angry] at temperature 2, take 1, [Kaprao](https://paxalabs.com/voices/kaprao), Emotion tags: https://paxalabs.com/audio/directions/temperature/kaprao-angry-t2-s1-2-1.m4a
- [angry] at temperature 2, take 2, [Kaprao](https://paxalabs.com/voices/kaprao), Emotion tags: https://paxalabs.com/audio/directions/temperature/kaprao-angry-t2-s2-2-2.m4a

A low temperature suits a directed line that plays many times, such as an app prompt or a game character's reply. A high one gives a scene several different takes to choose from.

In the console, the Speech tool shows Temperature as a slider under Speed, marked More stable at 0 and More creative at 2. Between them it reads Default at 0.5 and offers a Reset button anywhere else. A deep link or a shared link keeps the value.

On the API, `temperature` is a field of POST /v1/tts, of [the OpenAI-compatible speech endpoint](https://paxalabs.com/docs/openai-compatibility), and of the start frame on [a live connection](https://paxalabs.com/docs/live-streaming). A request costs the same at every temperature.

curl:

```bash
curl -X POST https://api.paxalabs.com/v1/tts \
  -H "Authorization: Bearer $PAXA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "สวัสดีครับ ยินดีต้อนรับสู่ Paxa Labs",
    "voice": "khanomkrok",
    "model": "paxa-tts-flash-v2",
    "temperature": 0
  }' \
  --output speech.mp3
```

TypeScript:

```typescript
import { writeFile } from "node:fs/promises";

const response = await fetch("https://api.paxalabs.com/v1/tts", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PAXA_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    text: "สวัสดีครับ ยินดีต้อนรับสู่ Paxa Labs",
    voice: "khanomkrok",
    model: "paxa-tts-flash-v2",
    temperature: 0,
  }),
});

if (!response.ok) throw new Error(`TTS failed: ${response.status}`);
await writeFile("speech.mp3", Buffer.from(await response.arrayBuffer()));
```

Python:

```python
import os
import requests

response = requests.post(
    "https://api.paxalabs.com/v1/tts",
    headers={"Authorization": f"Bearer {os.environ['PAXA_API_KEY']}"},
    json={"text": "สวัสดีครับ ยินดีต้อนรับสู่ Paxa Labs", "voice": "khanomkrok", "model": "paxa-tts-flash-v2", "temperature": 0},
)
response.raise_for_status()

with open("speech.mp3", "wb") as f:
    f.write(response.content)
```

Paxa TTS Flash v2 reads temperature. Paxa TTS Flash keeps its own variation and accepts only a value that rounds to `0.5`. Any other answers `400 validation` before anything is charged. [The temperature guide](https://paxalabs.com/docs/temperature) covers designed voices and retries under an Idempotency-Key.

## Pause tags

A pause tag adds silence after the word before it. Write a named length, `[short pause]`, `[pause]`, or `[long pause]`, or an exact one such as `[pause 300ms]`. Every Paxa TTS Flash v2 voice reads them, and Paxa TTS Flash reads none.

One line read by Nom Yen, with each pause:

- No pause, [Nom Yen](https://paxalabs.com/voices/nomyen), Lead voice: https://paxalabs.com/audio/directions/winner/nomyen-plain.m4a
- [short pause], [Nom Yen](https://paxalabs.com/voices/nomyen), Lead voice: https://paxalabs.com/audio/directions/winner/nomyen-short.m4a
- [pause], [Nom Yen](https://paxalabs.com/voices/nomyen), Lead voice: https://paxalabs.com/audio/directions/winner/nomyen-pause.m4a
- [long pause], [Nom Yen](https://paxalabs.com/voices/nomyen), Lead voice: https://paxalabs.com/audio/directions/winner/nomyen-long.m4a
- [pause 500ms], [Nom Yen](https://paxalabs.com/voices/nomyen), Lead voice: https://paxalabs.com/audio/directions/winner/nomyen-exact.m4a

A named pause takes its length from the language of the word before the tag.

| Language | `[short pause]` | `[pause]` | `[long pause]` |
| --- | --- | --- | --- |
| Thai | 130 ms | 180 ms | 460 ms |
| Chinese | 100 ms | 140 ms | 360 ms |
| English and other languages | 120 ms | 170 ms | 460 ms |

- `[pause 300ms]`, `[pause 0.3s]`, and `[pause:300]` are the same pause. A number with no unit is milliseconds.
- An exact length rounds to the nearest 10 ms, up to 500 ms. A longer one, such as `[pause 1s]`, plays at 500 ms.
- A pause adds to the gap the reading already leaves after that word. After a full stop, the same tag makes a longer silence than inside a sentence.
- A pause keeps its length at every speed.

Pause tags are text. Their characters count toward the request's length and its charge like any other. [The pause guide](https://paxalabs.com/docs/pauses) has the rest, including live connections.

## Also in this release

`[emphasis]` now reads as a direction, like `[whispering]` or `[excited]`.

## Questions

### Does temperature change the price?

A request costs the same at every temperature, from 0 to 2.

### How close are two takes at 0?

Closer than at any other setting, in timing, pauses, and melody. They are not guaranteed to match.

### Can a pause run longer than 500 ms?

One tag plays at most 500 ms. A longer tag plays at 500 ms and is not refused.

# Voice design is available now

Voice design turns a written description into a new Paxa TTS Flash v2 voice that you can change in words and save for the console and the API.

Published 2026-10-22. Paxa Labs.

Until today, every Paxa speech request used one of the 50 Paxa TTS Flash v2 voices in the catalog. A product that needed a particular sound took the closest of them.

Voice design adds voices made to a description. Type one in the console, such as “A documentary narrator in his fifties with a low, even voice and an unhurried pace.” The tool answers with one voice. It has a name, a role, and a short description, and it speaks a line written for it. Type what to change, and the next version plays. Save the one you want, and it works in the Speech tool and on the API.

Voice design is open to every account, free accounts included. Each design or refine turn costs 5 credits, its spoken line included. Speech in a saved voice runs on Paxa TTS Flash v2 at 1.25 times the standard rate, which is $12.50 per million characters.

## How a voice is designed

1. Describe it: Type who is speaking and how they sound, or start from one of the four examples in the tool: a documentary narrator, a café barista, a support agent, and a bedtime storyteller.
2. Listen: The name, role, and description fill in first. The voice then speaks a short line in character, in the language it was designed for.
3. Refine it: Type a change such as “warmer” or “a little older”, and the next version plays. A line you type, up to 120 characters, plays in the current version.
4. Save it: Name the voice and save it. It appears in the Speech tool under Your voices and gets an id for the API.

## What to put in a description

A description can name who is speaking, how they speak, the language, and where the voice will be used. The tool chooses whatever the description leaves out.

| Short description | Fuller description |
| --- | --- |
| A friendly woman | A friendly pharmacist in her thirties who explains doses slowly and clearly in Thai |
| A deep male voice | A late night radio host in his forties, deep and relaxed, close to the microphone, speaking English |
| An energetic voice for ads | A quick, bright host for a Bangkok street food channel who laughs between lines, speaking Thai |
| A narrator | A calm narrator for a history podcast, unhurried, with a slight smile in the voice, speaking English |

Name the language the voice should speak, English, Thai, or Mandarin. Without one, the tool takes it from the rest of the description. A café in Bangkok, for example, can produce a Thai speaker.

A famous voice is best described by its qualities. A description that names a real person gets an original voice, under a name the tool makes up.

## Refining a voice

A refinement is one instruction about the current voice, such as “make her sound a little older” or “slow the pace down”. The next version changes what the instruction asks for and keeps the rest.

Every version stays in the history panel, and going back to one costs nothing. A typed line plays in the current version without changing the voice, billed by the character at the designed voice rate.

## Directions in a designed voice

A designed voice speaks on Paxa TTS Flash v2 and reads its directions. `[whispering]` before a line, or `[excited]` before a sentence, works in a saved voice as it does in a catalog voice. The [direction guide](https://paxalabs.com/docs/directions) lists every direction and sound, and the [Paxa TTS Flash v2 launch post](https://paxalabs.com/blog/paxa-tts-flash-v2) plays them.

## Descriptions for common uses

| Use | A description to start from |
| --- | --- |
| Product videos and ads | A confident presenter in her thirties for a skincare brand, warm and quick, speaking Thai |
| Audiobooks and audio drama | An elderly innkeeper with a rough, slow voice who speaks English |
| Support lines and voice agents | A patient customer support agent with a warm, clear voice who speaks Thai at a steady pace |
| YouTube and TikTok narration | A narrator in his forties for a history channel, even and unhurried, speaking English |
| Children's apps | A grandfather telling a bedtime story in Thai, soft and slow, with a little humor |

A saved voice cannot be edited. A series made in it keeps one voice from the first episode to the last, and an ad cut into three lengths speaks in the same voice in each.

## Using a saved voice

- In the console, pick it in the Speech tool, where saved voices head the picker under Your voices.
- On `POST /v1/tts`, send its id, which starts with `voice_`, as `voice` with `model` set to `paxa-tts-flash-v2`.
- The [OpenAI-compatible](https://paxalabs.com/docs/openai-compatibility) `POST /v1/audio/speech` takes the same id. An OpenAI SDK pointed at the Paxa API speaks in the voice unchanged.
- The [live socket](https://paxalabs.com/docs/live-streaming), `/v1/tts/live`, takes the id in its `start` frame.

The id works for the account that saved it, from any of its API keys. Model marketplaces that resell Paxa Labs speech, and the demo on this site, serve catalog voices. The [designed voices guide](https://paxalabs.com/docs/designed-voices) has every rule of the id.

Renaming a voice keeps its id. Deleting one frees its place on the plan, and requests that still name it answer `unknown_voice`. After a downgrade, every saved voice keeps speaking, and new saves wait until the account is under its plan's limit again.

## Pricing

Each design or refine turn costs 5 credits, its spoken line included. A turn that fails without a voice is refunded, and a turn you stop keeps its charge. A new account's 100 free credits cover 20 turns. Speech in a designed voice bills at 1.25 times the Paxa TTS Flash v2 rate, or 1.5 times with word timestamps.

| Voice | Credits per 1,000 characters |
| --- | --- |
| A catalog voice | 10 |
| A designed voice | 12.5 |
| A designed voice with timestamps | 15 |

The plan sets how many designed voices an account keeps.

| Plan | Designed voices |
| --- | --- |
| Free | 1 |
| Starter | 3 |
| Standard | 10 |
| Pro | 50 |

Plans and their monthly credits are on the [pricing page](https://paxalabs.com/pricing).

## What it does not do

Voice design works from text alone. It takes no recording and cannot reproduce a person's voice from one. A description the tool cannot design from is refused, and the turn's credits are refunded.

Open [Voice design](https://paxalabs.com/app/voice-design) in the console to start.

## Questions

### Can I try voice design for free?

Yes. Every account can design voices and keep 1, free accounts included. A turn costs 5 credits, and a new account's 100 free credits cover 20 turns.

### How many voices can I keep?

Free accounts keep 1. Starter keeps 3. Standard keeps 10. Pro keeps 50.

### Can I edit a saved voice?

You can rename it. Its sound stays as you saved it, and a different sound is a new design.

### Does a designed voice work on Paxa TTS Flash?

No. Designed voices speak on Paxa TTS Flash v2. A Paxa TTS Flash request that names one answers `unknown_voice`.

### Can voice design clone my voice?

No. Voice design takes no recording. It designs a new voice from a description.

### Which language does a designed voice speak?

The one named in the description, English, Thai, or Mandarin. The description itself can be written in any of the three.

### Can I use a designed voice in the API?

Yes. Send its id as `voice` with `model` set to `paxa-tts-flash-v2`, from any API key on the account that saved it.

# Paxa TTS Flash v2 is available now

Paxa TTS Flash v2, nicknamed Pixie, performs the directions written into a line, such as a whisper or a laugh, in 50 voices that each speak English, Thai, and Mandarin, at the price of Paxa TTS Flash.

Published 2026-10-16, updated 2026-10-23. Paxa Labs.

A script tells a voice actor how each line should sound, whispered or close to tears. A speech model that reads every line in one even tone leaves those notes on the page, and the performance has to come from a recording session.

Today we are releasing Paxa TTS Flash v2. Inside the lab it goes by Pixie, and the nickname sits beside the model name in the console's Speech tool. Pixie performs the notes a script carries. A direction in square brackets, written in English, Thai, or Chinese, sets how the words after it are delivered, and the delivery can change several times inside one sentence.

Paxa TTS Flash v2 is available now on every speech route of the API and in the console, at $10 per million characters. That is the rate of Paxa TTS Flash, and requests to Paxa TTS Flash keep working as they do today.

The launch film. Every voice in it was generated from the text on screen.: https://paxalabs.com/video/paxa-tts-flash-v2-launch-en.mp4

## What changed from Paxa TTS Flash

|  | Paxa TTS Flash | Paxa TTS Flash v2 |
| --- | --- | --- |
| Delivery | Emotion tags such as `[happy]`, on 2 experimental voices | Directions in any words, on every voice |
| Sounds | None | Sounds such as `[laughs]`, played where written |
| Hesitations | None | `[um]` and `[hmm]`, spoken as words |
| Emphasis | None | `<emph>` and `<whisper>` spans |
| Languages | Each voice made for one of English, Thai, and Mandarin | Every voice speaks all three |
| Voices | 34 | 50 |
| mp3 | 24 kHz, 64 kbps | 44.1 kHz, 128 kbps |
| opus | 48 kHz, 48 kbps | 48 kHz, 64 kbps |
| Price per 1,000 characters | 10 credits | 10 credits |

## One sentence on each model

Foi Thong reads the same sentence twice. On Paxa TTS Flash the request carries the text alone. On Paxa TTS Flash v2 it carries two directions, `[whispering]` before the first half and `[ecstatic]` before the second.

Foi Thong, one sentence on each model:

- Paxa TTS Flash: “I can't believe it. We actually did it!” https://paxalabs.com/audio/takes/v1.m4a
- Paxa TTS Flash v2: “[whispering] I can't believe it. [ecstatic] We actually did it!” https://paxalabs.com/audio/takes/v2.m4a

## Directions in plain words

A direction is any words in square brackets, such as `[sad]`, `[speaking slowly, close to the microphone]`, or `[เสียงสดใส]`. It can be one word or a description of the whole read, as in `[product advertisement, punchy and upbeat]` or `[ผู้ประกาศข่าว น้ำเสียงเป็นทางการ]`. A direction itself is not spoken.

A direction holds until the next one. Text before the first direction keeps the voice's own delivery, and `[neutral]` returns to it. Case, extra spaces, and full-width brackets make no difference: `[Sad]`, `[ sad ]`, and `［sad］` are one direction.

The first line below changes delivery four times in one sentence. Foi Thong starts in a whisper, turns excited at a weight of 2, breaks into tears, and ends on a laugh.

Directions inside a line:

- Foi Thong: “[whispering] I was not going to tell anyone, [excited:2] but we got the house, [crying] after three years of saving. [laughs]” https://paxalabs.com/audio/takes/sw-2.m4a
- Mocha: “[whispering] Thirty seconds. Don't touch anything.” https://paxalabs.com/audio/takes/heist-2.m4a
- Mocha: “[shouting urgently] Run! Go, go, go!” https://paxalabs.com/audio/takes/heist-4.m4a
- Sanae Chan: “[เสียงสั่น] กลับมาแล้วเหรอ [กลั้นน้ำตา] ฉันนึกว่าคุณจะไม่กลับมาแล้ว” https://paxalabs.com/audio/takes/novel-3.m4a

### Weights and blends

A weight after a colon sets how strongly a direction or a sound applies, written as a number or a percentage: `[sad:0.5]`, `[sad:50%]`, `[laughs:0.3]`. A plus sign blends directions, each with its own weight, as in `[happy:0.7+sad:0.3]`.

## Sounds, hesitations, and emphasis

Sounds play where they are written: `[sighs]`, `[laughs]`, `[chuckles]`, `[exhales sharply]`, `[gasps]`, `[inhales deeply]`, `[giggles]`, `[sobs]`, `[yawns]`, and `[clears throat]`, along with spellings such as `[inhales]` and `[scoffs]`.

`[um]` and `[hmm]` are hesitations. Each is spoken as the word inside its brackets, and Thai spellings such as `[เอ่อ]` read the same way.

`<emph>` puts the stress of a sentence on the words inside it, and `<whisper>` whispers them. Its close returns the line to the delivery in force before the mark. Emphasis can also be written as a bracket, `[emphasize]` or `[เน้นเสียง]`.

One stressed word:

- Cookie: “[firm] I didn't eat your <emph>cookie</emph>.” https://paxalabs.com/audio/takes/em-cookie.m4a

## What it will not do

A tag the model cannot perform is refused before anything is charged, with `400 tag_invalid`. A text that is only tags, with no words to speak, answers `422 unspeakable_text`, also before the charge.

| Asked for | Examples |
| --- | --- |
| An accent | `[british accent]`, `[สำเนียงอีสาน]`, `[北京口音]` |
| A different voice | `[robotic]`, `[old man voice]`, `[เสียงเด็ก]` |
| Singing | `[singing]`, `[ร้องเพลง]`, `[唱歌]` |
| A sound effect | `[door slams]`, `[applause]` |
| A language | `[in thai]`, `[english]`, `[中文]` |
| A sound outside the list | `[sniffs]`, `[gulps]`, `[coughs]`, and `[sneezes]` |

The [direction guide](https://paxalabs.com/docs/directions) lists every sound, spelling, and refused tag.

## 50 voices, each in three languages

Paxa TTS Flash v2 has 50 voices: 26 carried over from Paxa TTS Flash under the same ids, and 24 new. On Paxa TTS Flash, each voice is made for one language. On Paxa TTS Flash v2, every voice speaks English, Thai, and Mandarin, and one voice can read a product in all three markets.

Pixie reads mixed-language text in one voice. Each word is read in the language of its script. A Thai sentence that names an English product, or a Mandarin notice with a flight number in it, goes out as one request. The optional `language` field sets how codes, digits, and symbols are read, such as AB2039 with Thai letter names under `"th"`.

One ad in three languages, then one agent in two:

- Khanom Krok: “[product advertisement, punchy and upbeat] New season. New colors. In stores this Friday.” https://paxalabs.com/audio/takes/loc-en.m4a
- Khanom Krok: “[โฆษณาสินค้า น้ำเสียงกระฉับกระเฉง] ซีซันใหม่ สีใหม่ วางขายวันศุกร์นี้” https://paxalabs.com/audio/takes/loc-th.m4a
- Khanom Krok: “[广告配音，热情洋溢] 新一季，新色彩，本周五上市。” https://paxalabs.com/audio/takes/loc-zh.m4a
- Cookie: “[apologetic, sincere] I'm so sorry about the wait. I've moved you to TG628 at 15:40, at no charge.” https://paxalabs.com/audio/takes/call-2.m4a
- Cookie: “[亲切的客服语气] 王太太您好，航班已改为TG628，15:40在12号登机口登机。” https://paxalabs.com/audio/takes/call-4.m4a

### The 24 new voices

| Voice | Cast for |
| --- | --- |
| [Saku](https://paxalabs.com/voices/saku) | AI assistant |
| [Satay](https://paxalabs.com/voices/satay) | Anime hero |
| [Thong Yod](https://paxalabs.com/voices/thongyod) | Anime heroine |
| [Bai Toey](https://paxalabs.com/voices/baitoey) | Mentor |
| [Kluay Tod](https://paxalabs.com/voices/kluaytod) | Cartoon sidekick |
| [Matum](https://paxalabs.com/voices/matum) | Documentary |
| [Hor Mok](https://paxalabs.com/voices/hormok) | Fantasy knight |
| [Nam Manao](https://paxalabs.com/voices/nammanao) | Fitness coach |
| [Khao Tom](https://paxalabs.com/voices/khaotom) | Family man |
| [Salim](https://paxalabs.com/voices/salim) | Girl next door |
| [Krayasat](https://paxalabs.com/voices/krayasat) | Grandfather |
| [Miang Kham](https://paxalabs.com/voices/miangkham) | Grandmother |
| [Tod Mun](https://paxalabs.com/voices/todmun) | Host and MC |
| [Nam Prik](https://paxalabs.com/voices/namprik) | Husky alto |
| [Khai Jiao](https://paxalabs.com/voices/khaijiao) | Hype commentator |
| [Chu Chi](https://paxalabs.com/voices/chuchi) | Professor |
| [Krong Kraeng](https://paxalabs.com/voices/krongkraeng) | Robot |
| [Chor Muang](https://paxalabs.com/voices/chormuang) | Bashful |
| [Khao Lam](https://paxalabs.com/voices/khaolam) | Soft romantic |
| [Pla Thu](https://paxalabs.com/voices/plathu) | Student |
| [Kaeng Phet](https://paxalabs.com/voices/kaengphet) | Trailer voice |
| [Kaeng Pa](https://paxalabs.com/voices/kaengpa) | Villain |
| [Makham](https://paxalabs.com/voices/makham) | Villainess |
| [Pui Fai](https://paxalabs.com/voices/puifai) | Virtual idol |

Each voice's sample plays on the [voices page](https://paxalabs.com/voices). 25 pages play a whole scene in these voices, from [anime voices](https://paxalabs.com/anime-voices) and [bedtime stories](https://paxalabs.com/bedtime-stories) to [sports commentary](https://paxalabs.com/sports-commentary) and a [robot voice](https://paxalabs.com/robot-voice).

## Scenes from the film

### A support call

A caller is frustrated by a long hold, the agent apologizes and rebooks the flight, and the caller asks for the news in Mandarin for his wife. Cookie answers in English and then in Mandarin, in one voice.

A support call:

- Espresso: “[frustrated, tense] I've been on hold for forty minutes. My flight leaves in an hour.” https://paxalabs.com/audio/takes/call-1.m4a
- Cookie: “[apologetic, sincere] I'm so sorry about the wait. I've moved you to TG628 at 15:40, at no charge.” https://paxalabs.com/audio/takes/call-2.m4a
- Espresso: “[relieved] Thank you. Could you tell my wife? She only speaks Mandarin.” https://paxalabs.com/audio/takes/call-3.m4a
- Cookie: “[亲切的客服语气] 王太太您好，航班已改为TG628，15:40在12号登机口登机。” https://paxalabs.com/audio/takes/call-4.m4a

### An audio drama

Two thieves move from a whispered countdown to panic, a shouted escape, and breathless relief. Each line carries one direction, and the last ends on a laugh.

An audio drama:

- Mocha: “[whispering] Thirty seconds. Don't touch anything.” https://paxalabs.com/audio/takes/heist-2.m4a
- Latte: “[panicked, fast] Too late. I touched something.” https://paxalabs.com/audio/takes/heist-3.m4a
- Mocha: “[shouting urgently] Run! Go, go, go!” https://paxalabs.com/audio/takes/heist-4.m4a
- Latte: “[out of breath, relieved] We're out. Nobody saw us. [laughs]” https://paxalabs.com/audio/takes/heist-5.m4a

### A Thai audio novel

A reunion in Thai, directed in Thai. The lines shake, hold back tears, and laugh through them.

A Thai audio novel:

- Sanae Chan: “[เสียงสั่น] กลับมาแล้วเหรอ [กลั้นน้ำตา] ฉันนึกว่าคุณจะไม่กลับมาแล้ว” https://paxalabs.com/audio/takes/novel-3.m4a
- Massaman: “[อบอุ่น] ผมสัญญาไว้แล้วไง ว่าจะกลับมา” https://paxalabs.com/audio/takes/novel-4.m4a
- Sanae Chan: “[หัวเราะทั้งน้ำตา] คนบ้า ปล่อยให้รอตั้งนาน” https://paxalabs.com/audio/takes/novel-5.m4a

### The evening news

A Thai news read:

- Thong Ek: “[ผู้ประกาศข่าว น้ำเสียงเป็นทางการ] สวัสดีครับ ข่าวภาคค่ำวันนี้ เริ่มต้นที่กรุงเทพมหานคร” https://paxalabs.com/audio/takes/reel-news.m4a

## Word timestamps

Set `timestamps` to `"word"` and the response carries every word with its start and end in seconds, for captions and for words that light as they are spoken, as on this page. `"sentence"` and `"utterance"` time longer spans. The spans cover the spoken text, and a tag takes none.

A buffered timed request returns JSON with the audio as base64 beside `timing.spans`. A streamed one returns one JSON line per chunk, each carrying its audio and the spans it completed. Timed requests bill at 125 percent of the speech rate, and the [timestamps guide](https://paxalabs.com/docs/timestamps) covers both shapes.

## Speed and streaming

In our own measurements from Singapore, the first audio of a request arrived in as little as 90 ms. With `stream` set to true, a request starts playing while the rest of the line is still being synthesized.

The [live socket](https://paxalabs.com/docs/live-streaming) speaks text while a language model is still writing it, and directions work there too. A tag still open when the socket flushes waits until its bracket closes, and each leg opens in the directions the last one ended in. A `cancel` frame stops the reply when a caller talks over it. The [real-time speech page](https://paxalabs.com/realtime-tts) walks through a session frame by frame.

## The same price

Paxa TTS Flash v2 costs 10 credits per 1,000 characters, which is $10 per million, the rate of Paxa TTS Flash. A tag bills as the characters it is written with, brackets included. Timed requests, and live connections that ask for timestamps, bill at 125 percent of the rate on both models.

| Line as sent | Characters | Credits |
| --- | --- | --- |
| I can't believe it. We actually did it! | 39 | 0.39 |
| [whispering] I can't believe it. [ecstatic] We actually did it! | 63 | 0.63 |
| [whispering] I was not going to tell anyone, [excited:2] but we got the house, [crying] after three years of saving. [laughs] | 125 | 1.25 |
| [โฆษณาสินค้า น้ำเสียงกระฉับกระเฉง] ซีซันใหม่ สีใหม่ วางขายวันศุกร์นี้ | 69 | 0.69 |
| [广告配音，热情洋溢] 新一季，新色彩，本周五上市。 | 26 | 0.26 |

A new account's 100 free credits voice 10,000 characters, about 16 minutes of Thai speech.

## Start using it

- `POST /v1/tts` returns buffered or streamed audio. Set `model` to `paxa-tts-flash-v2`.
- The [OpenAI-compatible](https://paxalabs.com/docs/openai-compatibility) `POST /v1/audio/speech` reads tags in `input` under the same rules. An OpenAI SDK pointed at the Paxa API sends directions as written.
- The [live socket](https://paxalabs.com/docs/live-streaming), `/v1/tts/live`, takes the model in its `start` frame.
- In the console, the Speech tool lists Paxa TTS Flash v2, with Pixie beside its name.

curl:

```bash
curl -X POST https://api.paxalabs.com/v1/tts \
  -H "Authorization: Bearer $PAXA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "[warm, unhurried] ยินดีต้อนรับกลับมาครับ [chuckles] วันนี้มีข่าวดี [เอ่อ] <whisper>แต่ขอเก็บเป็นความลับไว้ก่อนนะครับ</whisper>",
    "voice": "khanomkrok",
    "model": "paxa-tts-flash-v2"
  }' \
  --output speech.mp3
```

TypeScript:

```typescript
import { writeFile } from "node:fs/promises";

const response = await fetch("https://api.paxalabs.com/v1/tts", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PAXA_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    text: "[warm, unhurried] ยินดีต้อนรับกลับมาครับ [chuckles] วันนี้มีข่าวดี [เอ่อ] <whisper>แต่ขอเก็บเป็นความลับไว้ก่อนนะครับ</whisper>",
    voice: "khanomkrok",
    model: "paxa-tts-flash-v2",
  }),
});

if (!response.ok) throw new Error(`TTS failed: ${response.status}`);
await writeFile("speech.mp3", Buffer.from(await response.arrayBuffer()));
```

Python:

```python
import os
import requests

response = requests.post(
    "https://api.paxalabs.com/v1/tts",
    headers={"Authorization": f"Bearer {os.environ['PAXA_API_KEY']}"},
    json={"text": "[warm, unhurried] ยินดีต้อนรับกลับมาครับ [chuckles] วันนี้มีข่าวดี [เอ่อ] <whisper>แต่ขอเก็บเป็นความลับไว้ก่อนนะครับ</whisper>", "voice": "khanomkrok", "model": "paxa-tts-flash-v2"},
)
response.raise_for_status()

with open("speech.mp3", "wb") as f:
    f.write(response.content)
```

A request takes up to 5,000 characters and returns mp3, opus, wav, and pcm, with wav and pcm at 24 kHz. `speed` runs from 0.5 to 1.5 and changes no charge.

## Moving from Paxa TTS Flash

- Paxa TTS Flash stays available as `paxa-tts-flash-v1`, and its requests work as before.
- To move a request, change `model` to `paxa-tts-flash-v2`. The 26 voices on both models keep their ids.
- Pad Thai, Yoyo, Som Tam, Larb, Khao Soi, Roti, Donut, and Toast stay on Paxa TTS Flash.
- On Paxa TTS Flash, only its experimental voices read emotion tags such as `[happy]`. On Paxa TTS Flash v2, every voice reads tags, and `[happy]` is a direction like any other.

## Questions

### Is Paxa TTS Flash v2 a preview?

No. Paxa TTS Flash v2 is generally available, on the API and in the console.

### Does Paxa TTS Flash v2 cost more than Paxa TTS Flash?

No. Both cost 10 credits per 1,000 characters.

### Do requests use the name Pixie?

No. Pixie is the model's nickname. Requests name it by its id, `paxa-tts-flash-v2`.

### What can a direction say?

Any delivery, in English, Thai, or Chinese words, such as `[tired, exhausted]` or `[ผู้ประกาศข่าว น้ำเสียงเป็นทางการ]`. A direction that asks for an accent, a different voice, singing, a sound effect, or a language answers `tag_invalid`.

### Can I clone a voice?

No. Paxa TTS Flash v2 has 50 catalog voices and accepts no reference audio. A new voice comes from [voice design](https://paxalabs.com/voice-design), built from a written description.

# Paxa TTS Flash now costs a third less

From October 1, Paxa TTS Flash costs 10 credits per 1,000 characters, after a September that brought transcription, Document Extraction, Mandarin Chinese, and emotion tags.

Published 2026-10-01. Paxa Labs.

On August 3 we opened our first public API, one speech model that read Thai and English. Eight weeks later Paxa Labs runs 4 product lines on 7 models, more than 800 accounts have signed up, and more than 20 updates have shipped across the platform and the models.

## Speech costs a third less

From October 1, Paxa TTS Flash costs 10 credits per 1,000 characters, which is $10 per million characters. Until September 30 the rate was 15 credits, or $15 per million.

The new rate applies to every speech request, whichever way it arrives: `POST /v1/tts`, the OpenAI-compatible `POST /v1/audio/speech`, the [live socket](https://paxalabs.com/docs/live-streaming), and the Speech tool in the console. Requests with timestamps and live streaming sessions cost a third less as well. The model id, the voices, and the request format stay the same, and no code needs to change.

Credits already in a balance or a plan keep their value and now voice 50 percent more text. A $10 refill pack voiced about 18 hours of Thai speech at the old rate. It voices about 27 hours now.

| Credits | Characters from October 1 | Characters until September 30 |
| --- | --- | --- |
| 100 free credits on a new account | 10,000 | 6,666 |
| $10 refill pack, 10,000 credits | 1,000,000 | 666,000 |
| $6 monthly plan, 8,100 credits | 810,000 | 540,000 |
| $21 monthly plan, 25,200 credits | 2,520,000 | 1,680,000 |
| $99 monthly plan, 110,000 credits | 11,000,000 | 7,333,000 |

Most of September went into optimizing speech. The new price is the result, and it applies to every account from the first request.

## What shipped in September

Paxa TTS Flash was the only model when the API opened. Since then 6 more have shipped, most of them in September.

### Transcription, recorded and realtime

Paxa STT Lite opened on September 9 as a research preview. It transcribes Thai and English recordings through `POST /v1/stt`, word for word in the language spoken, with timestamps that cover whole Thai words. Speakers are numbered, and a vocabulary of up to 50 terms keeps names and product terms spelled the way you write them. It costs 8.33 credits per minute of audio.

A day later the same endpoint began returning subtitles as SRT or VTT files. Cues break at a change of speaker, at the end of a sentence, and at a pause, and a Thai line breaks only between words.

On September 23, [Paxa STT Lite Realtime](https://paxalabs.com/blog/realtime-transcription-for-thai-voice-agents) added transcription while the speaker is still talking. It takes audio over a WebSocket and detects the end of each turn itself, which tells a voice agent when to answer. It costs 12.5 credits per minute of audio received.

### Paxa Document Extraction

[Paxa Document Extraction](https://paxalabs.com/document-extraction) shipped on September 12. You send a PDF or an image with a JSON schema of the fields you need, such as an invoice number, a date, and the line items. The schema comes back filled in. Each value is the text printed on the page, or null with the reason it is missing.

A page costs 13 credits for a schema of up to 50 leaf fields and 19.5 for a larger one, with the reading of the page included.

### Mandarin Chinese, emotion tags, and a language setting

On September 14, Paxa TTS Flash [began reading Mandarin Chinese](https://paxalabs.com/blog/mandarin-chinese-and-six-new-voices) from Simplified script beside Thai and English. Six voices joined the roster that day, two for each language.

On September 25, [two experimental voices](https://paxalabs.com/blog/emotion-tags-for-thai-speech), Khanom Chan and Kaprao, began reading emotion tags written into the text. There are 26 tags, such as `[happy]` and `[furious]`, and a weight such as `[sad:0.5]` sets how strongly a tag lands. The roster now holds 34 voices.

On October 1, every speech request gained a language setting. Set it to the language the text is written in, and numbers, symbols, and codes such as AB2039 are read the way that language reads them. Auto, the default, works the language out from the text.

Earlier in September, the live socket gained a cancel message that stops a reply mid-sentence. An assistant can now stop talking the moment its user talks over it.

### Tools for developers and agents

The [MCP server](https://paxalabs.com/docs/mcp), `@paxalabs/mcp`, gives Claude Code, Cursor, and other MCP clients the API as tools. They can speak through the computer's speakers, save speech to a file, translate into Thai, and read documents with OCR.

[Feedback](https://paxalabs.com/docs/api/feedback) now goes through the API as well. `POST /v1/feedback` reports a wrong output or an API problem against the request ids it names, and `GET /v1/feedback` lists each report with its status and a note from our team.

## Time to first audio

The time a request takes to return its first audio is the delay a listener notices first in a voice agent or a live reply. [A measured run of the public API](https://paxalabs.com/blog/tts-latency-singapore-bangkok) from Singapore and Bangkok records it, with its date, client locations, and request shapes.

## Independent benchmarks

[Speko's multilingual text to speech benchmark](https://benchmarks.speko.ai/tts-multilingual?lang=th) includes Paxa TTS Flash on its Thai board. Speko runs the board and publishes its method beside the results.

## Try it at the new rate

The new rate is live on every account. The 100 free credits on a new account now voice 10,000 characters, about 16 minutes of Thai speech, in the Speech tool or through the API. [Pricing](https://paxalabs.com/pricing) lists every rate and plan, and the [changelog](https://paxalabs.com/docs/changelog) dates each release.

# Paxa TTS Flash now reads emotion tags, on two new voices

Khanom Chan and Kaprao join Paxa TTS Flash as experimental voices that read 26 emotion tags written into the text, with a weight for how strongly each one lands.

Published 2026-09-25, updated 2026-10-01. Paxa Labs.

A Thai reply carries its warmth in the words. Read in the level tone of a delivery update, ดีใจด้วยนะ sounds like someone reading a script. From September 25, two voices in Paxa TTS Flash take the delivery from the text as well. Write [happy] before the line, and the line is spoken happy.

The two voices, with no tags:

- [Khanom Chan](https://paxalabs.com/voices/khanomchan), Emotion tags: https://paxalabs.com/audio/khanomchan.m4a
- [Kaprao](https://paxalabs.com/voices/kaprao), Emotion tags: https://paxalabs.com/audio/kaprao.m4a

## What changed

- [Khanom Chan](https://paxalabs.com/voices/khanomchan) (`khanomchan`) and [Kaprao](https://paxalabs.com/voices/kaprao) (`kaprao`) join the roster as experimental voices, for 34 in all.
- Both read 26 emotion tags written into `text`, such as `[happy]`, `[sad]`, and `[furious]`. The other voices skip them.
- A weight after a colon, as in `[sad:0.5]`, sets how strongly a tag lands.
- `GET /v1/voices` adds `experimental` and `emotion_tags` to every voice.
- The model id stays `paxa-tts-flash-v1`, and the speech rate does not change.

## How a tag reads

A tag sets the delivery from where it stands until the next tag. Text before the first tag keeps the voice's own delivery, and one request changes delivery as often as its text needs.

one request, two deliveries:

```
POST /v1/tts

{ "model": "paxa-tts-flash-v1", "voice": "khanomchan",
  "text": "[happy] สอบผ่านแล้ว [sad:0.6] แต่คงคิดถึงที่นี่" }
```

The request above, in Khanom Chan:

- [Khanom Chan](https://paxalabs.com/voices/khanomchan), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/request.m4a

## The tags

The tags are everyday words for how a line should feel. Five name the broad emotions: `[neutral]`, `[happy]`, `[sad]`, `[angry]`, and `[afraid]`. The rest name a closer shade of one, such as `[annoyed]` or `[crying]`.

The five broad emotions on one line, in Khanom Chan:

- [neutral], [Khanom Chan](https://paxalabs.com/voices/khanomchan), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/khanomchan-neutral.m4a
- [happy], [Khanom Chan](https://paxalabs.com/voices/khanomchan), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/khanomchan-happy.m4a
- [sad], [Khanom Chan](https://paxalabs.com/voices/khanomchan), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/khanomchan-sad.m4a
- [angry], [Khanom Chan](https://paxalabs.com/voices/khanomchan), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/khanomchan-angry.m4a
- [afraid], [Khanom Chan](https://paxalabs.com/voices/khanomchan), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/khanomchan-afraid.m4a

The same line and tags in Kaprao:

- [neutral], [Kaprao](https://paxalabs.com/voices/kaprao), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/kaprao-neutral.m4a
- [happy], [Kaprao](https://paxalabs.com/voices/kaprao), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/kaprao-happy.m4a
- [sad], [Kaprao](https://paxalabs.com/voices/kaprao), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/kaprao-sad.m4a
- [angry], [Kaprao](https://paxalabs.com/voices/kaprao), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/kaprao-angry.m4a
- [afraid], [Kaprao](https://paxalabs.com/voices/kaprao), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/kaprao-afraid.m4a

Inside a group, the other words set how far the delivery goes. `[annoyed]` is a lighter angry, and `[shouting]` is the loudest. `[disappointed]` is a lighter sad, and `[crying]` sounds close to tears.

The angry group from lightest to loudest, in Kaprao:

- [annoyed], [Kaprao](https://paxalabs.com/voices/kaprao), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/kaprao-annoyed.m4a
- [angry], [Kaprao](https://paxalabs.com/voices/kaprao), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/kaprao-angry.m4a
- [furious], [Kaprao](https://paxalabs.com/voices/kaprao), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/kaprao-furious.m4a
- [shouting], [Kaprao](https://paxalabs.com/voices/kaprao), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/kaprao-shouting.m4a

The sad group, in Khanom Chan:

- [disappointed], [Khanom Chan](https://paxalabs.com/voices/khanomchan), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/khanomchan-disappointed.m4a
- [sad], [Khanom Chan](https://paxalabs.com/voices/khanomchan), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/khanomchan-sad.m4a
- [sorrowful], [Khanom Chan](https://paxalabs.com/voices/khanomchan), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/khanomchan-sorrowful.m4a
- [crying], [Khanom Chan](https://paxalabs.com/voices/khanomchan), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/khanomchan-crying.m4a

| Group | Tags |
| --- | --- |
| Neutral | `[neutral]`, `[calm]`, `[normal]`, `[plain]` |
| Happy | `[happy]`, `[excited]`, `[cheerful]`, `[joyful]`, `[amazed]`, `[laughing]` |
| Sad | `[sad]`, `[crying]`, `[sorrowful]`, `[gloomy]`, `[disappointed]` |
| Angry | `[angry]`, `[shouting]`, `[furious]`, `[annoyed]`, `[frustrated]` |
| Afraid | `[afraid]`, `[scared]`, `[fearful]`, `[nervous]`, `[terrified]`, `[anxious]` |

The [emotional voices page](https://paxalabs.com/emotional-voices) plays all 26 tags in both voices, and the [emotion tags guide](https://paxalabs.com/docs/emotion-tags) covers the grammar.

## How strongly a tag lands

A weight after a colon sets how strongly the tag applies, from 0 to 1. `[sad:0.5]` and `[sad:50%]` read the same. A weight past either end reads as that end, and a tag at 0 does not apply.

[sad] from 0 to 1, in Khanom Chan:

- [sad:0], [Khanom Chan](https://paxalabs.com/voices/khanomchan), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/dial-w0.m4a
- [sad:0.25], [Khanom Chan](https://paxalabs.com/voices/khanomchan), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/dial-w25.m4a
- [sad:0.5], [Khanom Chan](https://paxalabs.com/voices/khanomchan), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/dial-w50.m4a
- [sad:0.75], [Khanom Chan](https://paxalabs.com/voices/khanomchan), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/dial-w75.m4a
- [sad], [Khanom Chan](https://paxalabs.com/voices/khanomchan), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/dial-w100.m4a

## Tags in real lines

Each line below is the `text` of one request, sent as written. The clips are the audio those requests returned.

A companion's reply turns from congratulations to missing someone, then settles on a plan. The [AI companion guide](https://paxalabs.com/ai-companion) covers the register the words already carry.

text:

```
[happy] ได้ข่าวว่าสอบผ่านแล้ว ดีใจด้วยจริง ๆ นะ
[sad:0.6] แต่ก็ใจหายเหมือนกัน ต่อไปคงไม่ค่อยได้เจอกันแล้ว
[calm] ไม่เป็นไร เดี๋ยวเรานัดกินข้าวกันนะ
```

A companion's reply:

- [Khanom Chan](https://paxalabs.com/voices/khanomchan), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/companion.m4a

A guard in a text adventure snaps about an open gate, then worries about the captain. [Interactive fiction](https://paxalabs.com/use-cases/interactive-fiction) covers voicing a cast of characters.

text:

```
[furious] ใครเปิดประตูค่ายทิ้งไว้
[anxious:0.7] ถ้าหัวหน้ารู้เข้า พวกเราโดนหนักแน่
```

A guard in a game:

- [Kaprao](https://paxalabs.com/voices/kaprao), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/game.m4a

An audio novel keeps the narration calm until the back door opens. [Audio novels](https://paxalabs.com/use-cases/audio-novels) covers long chapters read in one voice.

text:

```
[calm] คืนนั้นฝนตกทั้งคืน บ้านทั้งหลังเงียบสนิท
[terrified] จนกระทั่งประตูหลังบ้านค่อย ๆ เปิดออกเอง
```

A line of narration:

- [Kaprao](https://paxalabs.com/voices/kaprao), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/story.m4a

A support agent apologizes for a late parcel, then gives the new delivery date. [Voice agents](https://paxalabs.com/use-cases/voice-agents) covers speech for phone and chat assistants.

text:

```
[disappointed:0.4] ขออภัยค่ะ พัสดุของคุณส่งช้ากว่ากำหนด
[cheerful] ตอนนี้ออกจากคลังแล้ว คาดว่าจะถึงพรุ่งนี้ค่ะ
```

A support reply:

- [Khanom Chan](https://paxalabs.com/voices/khanomchan), Emotion tags: https://paxalabs.com/audio/blog/emotion-tags-for-thai-speech/support.m4a

## Where tags work

- `POST /v1/tts`, buffered or streamed. With timestamps on, the spans cover the spoken text, and a tag takes none.
- `POST /v1/audio/speech`, the [OpenAI-compatible route](https://paxalabs.com/docs/openai-compatibility), reads tags in `input`.
- The [live socket](https://paxalabs.com/docs/live-streaming) reads a tag split across two frames whole. Each leg opens in the delivery the last one ended in, until `cancel` resets it.
- The Speech tool in the console has an Emotion panel with every tag and an intensity slider, and draws each tag in the voice's color as you type.

## What the API refuses

On the two voices, a bracketed word outside the list, such as `[laughs]`, or a weight that is not a number, such as `[sad:half]`, answers 400 `tag_invalid` before any charge. A word outside the list followed by a colon, such as `[Fig:2]`, is ordinary text and is spoken. Text made only of tags answers 422 `unspeakable_text` before any charge.

The other voices refuse no tag. They drop the tags on the list and read a bracketed word outside it aloud.

## What tags cost

Tags are text, and their characters bill at the [one speech rate](https://paxalabs.com/pricing), 10 credits per 1,000 characters. The request above is 47 characters with its tags, and it costs 0.47 credits.

## What experimental means

The voice ids and the tag list stay as they are. How each tag sounds may change while we tune the two voices, and a line generated later may sound different from the same line today. Every other voice keeps its delivery.

## Try it

- Open the Speech tool, pick Khanom Chan or Kaprao, and open the Emotion panel.
- Read the [emotion tags guide](https://paxalabs.com/docs/emotion-tags) for the grammar, the weights, and the live socket.

# Paxa TTS Flash starts speaking in 104 milliseconds

We measured time to first audio on Paxa TTS Flash from Google Cloud in Singapore and Bangkok: 104 ms and 131 ms at the median, with no failed requests.

Published 2026-09-25. Paxa Labs.

In conversation, the most common gap between one person finishing and the next person starting is under 200 milliseconds. A [study of ten languages](https://www.pnas.org/doi/10.1073/pnas.0903616106) found that pattern in every one of them. A voice agent answers inside the same gap, and the speech API spends part of it before any sound plays.

On September 24 we measured that part from two cities. Virtual machines in the Singapore and Bangkok regions of Google Cloud called the production API the way a customer's server does.

- Median time to first audio was 104 ms from Singapore and 131 ms from Bangkok.
- 98.3% of requests from Singapore and 91.2% from Bangkok returned first audio in under 200 ms.
- A 35.1-second Thai passage arrived complete within 750 ms.
- All 1,676 speech requests succeeded.

## Time to first audio

| Client location | Requests | p50 | p90 | p99 | Under 200 ms |
| --- | --- | --- | --- | --- | --- |
| Singapore | 299 | 104 ms | 157 ms | 324 ms | 98.3% |
| Bangkok | 284 | 131 ms | 195 ms | 304 ms | 91.2% |

Each request streamed a 93-character Thai line in the Nom Yen voice as MP3 from [POST /v1/tts](https://paxalabs.com/docs/api/text-to-speech), which is 6.9 seconds of audio. The clock starts when the client sends the request and stops at the first byte of audio. That span covers the network trip, authentication, and synthesis of the opening words.

The p99 column is the slowest request in a hundred. At that point first audio still arrived within 324 ms from Singapore and 304 ms from Bangkok.

## Where the time goes

The gap between the two cities is network distance. A bare round trip to the API's health endpoint took 16 ms at the median from Singapore and 42 ms from Bangkok. Subtract it from time to first audio and the two cities leave 88 and 89 ms. That remainder is the time the API spends between receiving a request and sending its first audio, and it is the same from either city.

## Long text starts as fast as short text

A streamed response starts from the opening of the text while the rest is still being synthesized. A 420-character Thai passage, 35.1 seconds of audio, returned its first audio in 76 ms from Singapore and 102 ms from Bangkok at the median. The whole passage finished arriving within 750 ms, about 47 times faster than it plays.

## English, Opus, and the OpenAI endpoint

The same run measured an English line, Opus audio, the OpenAI-compatible endpoint, and the longer Thai passage. Each figure below is time to first audio.

| Request | Singapore p50 | Singapore p90 | Bangkok p50 | Bangkok p90 |
| --- | --- | --- | --- | --- |
| Thai, MP3, 93 characters | 104 ms | 157 ms | 131 ms | 195 ms |
| English, MP3, 97 characters | 94 ms | 131 ms | 119 ms | 162 ms |
| Thai, Opus, 93 characters | 110 ms | 158 ms | 131 ms | 197 ms |
| Thai, OpenAI-compatible endpoint | 107 ms | 161 ms | 126 ms | 185 ms |
| Thai, MP3, 420 characters | 76 ms | 112 ms | 102 ms | 132 ms |

The English line used the Cookie voice and runs 5.3 seconds. The OpenAI-compatible endpoint, [POST /v1/audio/speech](https://paxalabs.com/docs/api/speech), landed within 5 ms of POST /v1/tts at the median from both cities. Code written for OpenAI's speech API gets the same first audio with a changed base URL.

## Complete audio

A voice-over rendered into a video needs the whole file before anything plays. Without streaming, the 6.9-second Thai line arrived as a complete file in 241 ms at the median from Singapore and 280 ms from Bangkok.

| Audio | Singapore p50 | Singapore p90 | Bangkok p50 | Bangkok p90 |
| --- | --- | --- | --- | --- |
| 6.9 s Thai line, one file | 241 ms | 362 ms | 280 ms | 396 ms |
| 35.1 s Thai passage, streamed to the end | 750 ms | 1,021 ms | 747 ms | 981 ms |

## How we measured

- The clients were two Google Cloud n4-standard-2 virtual machines, one in zone asia-southeast1-b (Singapore) and one in zone asia-southeast3-a (Bangkok).
- Both ran Node.js 26.3 and called api.paxalabs.com over HTTPS with an API key that had no credit limit. Each sent one request at a time on one connection kept open between requests.
- The two clients ran at the same time, from 10:31 to 10:38 UTC on September 24, 2026, with the tests interleaved.
- Time to first audio is measured at the client, from sending the request to receiving the first byte of the response body.
- Each row counts between 46 and 299 requests.
- Thai requests used Nom Yen and English requests used Cookie. The two short lines are the published sample lines of those voices.

## Hear it yourself

The [text to speech page](https://paxalabs.com/tts) speaks a line in any of its demo voices without an account. To call the API, [Optimize latency](https://paxalabs.com/docs/latency) lists the steps that run before the first byte and which of them a client controls.

The two voices in the test, on their published sample lines:

- [Nom Yen](https://paxalabs.com/voices/nomyen), Lead voice: https://paxalabs.com/audio/nomyen.m4a
- [Cookie](https://paxalabs.com/voices/cookie), Support agent: https://paxalabs.com/audio/cookie.m4a

# Paxa STT Lite Realtime transcribes Thai turn by turn, as it is spoken

Paxa STT Lite Realtime transcribes Thai over one open WebSocket, finds the end of each turn on its own, and bills the audio it receives.

Published 2026-09-24. Paxa Labs.

A Thai voice agent cannot answer until it knows the caller has finished. Thai gives it very little to go on. The writing carries no full stop, the spaces fall between phrases, and a speaker's pause lands mid-sentence about as often as it lands at the end of one. Until September 23, a team building on our API transcribed finished recordings and wrote that judgment itself.

Paxa STT Lite Realtime now transcribes speech as the audio arrives over one WebSocket, decides where each turn ended, and delivers one finished transcript for it. The model id is `paxa-stt-lite-realtime-v1-preview` and the endpoint is `WS /v1/stt/live`.

## What shipped

- A realtime model, `paxa-stt-lite-realtime-v1-preview`, served over `WS /v1/stt/live`. One connection carries a whole conversation.
- Turn detection inside the socket. `speech_started` opens a turn, `speech_ended` closes it, and one final `transcript` follows.
- Audio in signed 16-bit PCM at 16 or 48 kHz, or in telephony mu-law at 8 kHz.
- The language hint, word timestamps, the style, the convention, and the vocabulary of `POST /v1/stt`, all fixed for the connection at `start`.
- Billing on the audio received, silence included, at 12.5 credits a minute. A `charged` receipt lands as the conversation runs.
- A live mode in the [Transcribe tool](https://paxalabs.com/app/transcribe), which relays a browser microphone with no code at all.

## One session, unedited

The ledger below is a capture from 2026-09-23. Three call-centre prompts, generated with Paxa TTS Flash, were played into one connection with a pause between them. That is 29.95 seconds of audio. The turn boundaries, the transcripts, and the charges are what the socket reported, reproduced without edits.

| Turn | Seconds | Credits | Transcript |
| --- | --- | --- | --- |
| 1 | 0 to 2.7 | 0.57 | สวัสดีค่ะขอบคุณที่ติดต่อบริษัทของเรา |
| 2 | 2.7 to 8 | 1.1 | กด 1 เพื่อติดต่อฝ่ายขายกด 2 ฝ่ายบริการลูกค้าหรือกด 0 เพื่อติดต่อเจ้าหน้าที่ค่ะ |
| 3 | 9.11 to 18.05 | 2.1 | สวัสดีค่ะคุณสมชายยอดชำระของท่านจำนวน 1,250 บาทครบกำหนดวันที่ 15 สิงหาคมนี้ชำระผ่านแอปได้ตลอด 24 ชั่วโมงค่ะ |
| 4 | 19.16 to 22.9 | 1.01 | ขณะนี้อยู่นอกเวลาทำการเราเปิดให้บริการวันจันทร์ถึงศุกร์ |
| 5 | 22.9 to 23.71 | 0.16 | เวลา |
| 6 | 23.92 to 26.44 | 0.57 | 9 โมงเช้าถึง 6 โมงเย็น |
| 7 | 26.44 to 28.55 | 0.44 | ฝากข้อความหลังเสียงสัญญาณได้เลยค่ะ |

Read turns 4 through 6 together and they are one sentence about opening hours. The speaker paused before the word for time and again before the numbers, and the socket closed a turn at each pause. That is the Thai boundary problem showing up in a real capture. An agent holding its reply until the caller has been quiet for a moment reads all three and answers once.

The turns charged 5.95 credits between them. The connection kept listening for another 1.4 seconds after the last turn closed, which added 0.29. The total the socket reported was 6.24 credits.

## What travels on the socket

Three frames go up and seven events come down. Audio is binary and everything else is JSON, on the one connection.

turn 1 on the wire:

```
client -> {"type":"start","model":"paxa-stt-lite-realtime-v1-preview",
           "audio":{"encoding":"pcm_s16le","sample_rate":16000},
           "language":"th","convention":"written"}
server <- {"type":"started","connection":"7d9c2b7e","model":"paxa-stt-lite-realtime-v1-preview"}
client -> <binary audio frames>
server <- {"type":"speech_started","turn":1,"time":0.000}
server <- {"type":"transcript","turn":1,"is_final":false,"text":"สวัสดีค่ะ"}
server <- {"type":"speech_ended","turn":1,"time":2.700}
server <- {"type":"charged","seq":1,"seconds":2.700,"credits":0.57}
server <- {"type":"transcript","turn":1,"is_final":true,"text":"สวัสดีค่ะขอบคุณที่ติดต่อบริษัทของเรา"}
client -> {"type":"end"}
server <- {"type":"done","turns":7,"total_credits":6.24}
```

- `start` names the model and the audio encoding and carries every option for the session. It is the first frame, exactly one per connection.
- `playback` tells the socket when your agent's own voice is playing into the room the microphone is in.
- `end` closes the turn in progress, delivers its transcript, and asks for `done`.

The events coming down are the other half of the contract.

- `started` confirms the session and names the connection id. Quote that id in a support request.
- `speech_started` opens a turn and carries where the speech began on the audio clock.
- `transcript` with `is_final` false is an interim draft of the turn so far. It is replaced by the next one.
- `speech_ended` closes the turn.
- `charged` is a billing receipt for one period, with the seconds it covered and the running total.
- `transcript` with `is_final` true is the turn. This is the text a program acts on.
- `error` names a failed turn, or reports a fatal failure before the close.
- `done` carries the connection's totals and precedes a normal close.

## Turn detection, and the interruption signal

`speech_started` arrives once the socket has heard enough to be sure. The time it reports is therefore earlier than the moment it lands on your side. Treat the event as the interruption signal. An agent that is mid-sentence stops its own audio and cancels the reply it was generating.

`speech_ended` says the turn is closed and its final transcript is coming. A voice agent can show that it is thinking from here. The final follows once that turn's billing period has landed, which is what keeps a transcript from arriving ahead of its receipt.

> The `playback` frame is what keeps an agent from transcribing itself. Send `{"type":"playback","speaking":true}` when your agent's audio starts and `false` when it stops. While it is true, opening a turn takes more sustained speech from the caller, and a real interruption still opens one.

Turns count from 1 per connection, and a higher number is always a later turn. An agent that already answered turn 3 drops turn 3 and answers turn 4. That single comparison is the whole of the staleness rule.

## The options carry over

Everything `POST /v1/stt` takes as a request field, the socket takes once in `start` and holds for the life of the connection. None of it changes the charge.

| Field | What it does |
| --- | --- |
| `language` | A BCP 47 hint. Left out, the model detects the language from the audio and writes each language in its own script. |
| `timestamps` | `"word"` adds a span array to every final transcript, one entry per word on the connection's clock. Thai words are segmented for you. |
| `style` | `verbatim` writes fillers and false starts as they were said. `clean` drops them and reads more easily. Verbatim is the default and follows the audio most closely. |
| `convention` | `spoken` writes numbers as the caller said them. `written` puts them in digits with units and ๆ, which is the form a booking or a payment field reads. |
| `vocabulary` | Up to 50 terms of 50 characters the conversation is likely to contain, spelled the way they should appear. |

Speaker numbering and subtitle files stay on the recording endpoint. One pass over a whole recording is what numbers the speakers and cuts the cues, and a live connection has no whole recording to read.

## The audio the socket takes

| Encoding | Sample rate | Where it comes from |
| --- | --- | --- |
| `pcm_s16le` | 16 or 48 kHz | Signed 16-bit little-endian PCM, mono. A browser microphone through an audio worklet, or a capture device at its native rate. |
| `mulaw` | 8 kHz | G.711 mu-law, mono, as a telephony platform hands it over. No transcoding on your side. |

- Binary frames carry the bytes in any chunking, up to 64 KiB a frame. Twenty milliseconds is a common size for a microphone.
- Any cadence works. Sending faster than real time is fine, and the clock the events report is the audio clock either way.
- The bytes are read exactly as `start` declared them. A wrong sample rate transcribes noise.

> An API key never belongs in a browser. The upgrade is authenticated with the same `Authorization` header as every other endpoint, and a browser WebSocket cannot send one. Relay the microphone through your own server.

## What a session costs

A connection is billed for the audio it receives, silence included, at 12.5 credits a minute. That is 750 credits an hour, about $0.75. In smaller units it is one hundredth of a credit for every 48 milliseconds on the wire.

The charge is taken in periods. A period closes at the end of each turn, after a minute of audio with no turn in it, and at the close of the session. Each one arrives as a `charged` receipt. The 0.1-credit request floor applies once per connection, on the first receipt.

A turn the model could not transcribe costs nothing. Its audio is held back from the running total and an `error` event names the turn and the credits it returned. A connection that delivered no turn at all is refunded in full.

| Workload | Audio received | Credits | Cost |
| --- | --- | --- | --- |
| One 3-minute call | 3 minutes | 37.5 | $0.04 |
| 200 calls in a day | 10 hours | 7,500 | $7.50 |
| A 30-day month of them | 300 hours | 225,000 | $225 |

The 29.95-second capture above cost 6.24 credits. A new account's 100 free credits cover about 8 minutes of a live connection.

In [the request log](https://paxalabs.com/app/logs) the whole connection is one row, whatever it took in turns. The row carries the audio received, the credits charged, and the connection id the socket announced at `started`.

## When something fails

An `error` carrying a `turn` leaves the connection open. That turn delivers no transcript, its audio is held back from the bill, and the next turn proceeds. An agent can treat it the way it treats a caller it did not hear and ask again.

A fatal failure closes the socket with a code. Three of them are worth handling in code.

- `1001` is a restart on our side. Reconnect and keep going. Everything the delivered periods charged stays charged.
- `4429` means the account already holds too many live connections. Close one and reconnect.
- `4503` means the backend could not be reached at `start`. Nothing was charged. Retry.

The full close-code list, with every frame field, is in the [realtime reference](https://paxalabs.com/docs/api/stt-live). Every code an `error` event can carry is in the [error reference](https://paxalabs.com/docs/errors).

## Limits

- A connection accepts 4 hours of audio, then closes with `done` the way an `end` frame would.
- An open connection holds one of the plan's [concurrent transcription slots](https://paxalabs.com/docs/rate-limits) for as long as it stays connected.
- A binary frame is 64 KiB at most.
- A connection with no frame in and no event out for 300 seconds closes.

## Recording or realtime

The two models answer different questions. A file that already exists belongs on `POST /v1/stt`, which reads the whole recording before it answers and costs less a minute for doing so.

|  | Paxa STT Lite | Paxa STT Lite Realtime |
| --- | --- | --- |
| Transport | `POST /v1/stt` | `WS /v1/stt/live` |
| Input | A file up to 60 minutes and 25 MiB | Binary audio for up to 4 hours |
| Answer | One transcript for the whole recording | One final transcript per turn |
| Rate | 8.33 credits a minute | 12.5 credits a minute |
| Speaker numbering | Yes, counted from 0 | Recording endpoint only |
| Subtitle files | srt and vtt | Recording endpoint only |
| Charged | Once, before transcription | In periods, as the conversation runs |

Both models are a research preview. They are sold at the published rate while they are evaluated, and their output and options may change between releases. The ids stay stable for as long as they serve.

## Opening a session

One start frame names the model and the audio encoding. Everything after it is binary audio going up and events coming down, on a connection that stays open for the conversation.

TypeScript:

```typescript
import { open } from "node:fs/promises";

// Runs on Bun as-is; on Node, `import WebSocket from "ws"` accepts
// the same constructor options. Browsers cannot send this header, and
// API keys never ship to browsers anyway: relay the audio through your
// server.
const socket = new WebSocket("wss://api.paxalabs.com/v1/stt/live", {
  headers: { Authorization: `Bearer ${process.env.PAXA_API_KEY}` },
});

socket.onopen = async () => {
  socket.send(JSON.stringify({
    type: "start",
    model: "paxa-stt-lite-realtime-v1-preview",
    audio: { encoding: "pcm_s16le", sample_rate: 16000 },
    language: "th",
  }));
  // 16 kHz signed 16-bit mono is 32,000 bytes a second. A microphone
  // delivers frames at this pace on its own; a file has to be paced.
  const file = await open("caller.pcm");
  const frame = new Uint8Array(640); // 20 ms
  for (;;) {
    const { bytesRead } = await file.read(frame, 0, frame.length);
    if (bytesRead === 0) break;
    socket.send(frame.subarray(0, bytesRead));
    await new Promise((resolve) => setTimeout(resolve, 20));
  }
  socket.send(JSON.stringify({ type: "end" }));
};

socket.onmessage = (event) => {
  const message = JSON.parse(String(event.data));
  switch (message.type) {
    case "speech_started":
      // The user is talking: stop the agent's audio, cancel its reply.
      break;
    case "transcript":
      if (message.is_final) console.log(`turn ${message.turn}: ${message.text}`);
      break;
    case "done":
      socket.close();
  }
};
```

Python:

```python
import json
import os
import time

from websockets.sync.client import connect  # pip install websockets

with connect(
    "wss://api.paxalabs.com/v1/stt/live",
    additional_headers={"Authorization": f"Bearer {os.environ['PAXA_API_KEY']}"},
) as socket:
    socket.send(json.dumps({
        "type": "start",
        "model": "paxa-stt-lite-realtime-v1-preview",
        "audio": {"encoding": "pcm_s16le", "sample_rate": 16000},
        "language": "th",
    }))
    # 16 kHz signed 16-bit mono is 32,000 bytes a second; a file has to
    # be paced the way a microphone paces itself.
    with open("caller.pcm", "rb") as audio:
        while frame := audio.read(640):  # 20 ms
            socket.send(frame)
            time.sleep(0.02)
    socket.send(json.dumps({"type": "end"}))

    for message in socket:
        event = json.loads(message)
        if event["type"] == "transcript" and event["is_final"]:
            print(event["turn"], event["text"])
        elif event["type"] == "done":
            break
```

Server events:

```json
{"type":"started","connection":"7d9c2b7e-4a41-4c8e-9a35-1f26d8c04b7a","model":"paxa-stt-lite-realtime-v1-preview"}
{"type":"speech_started","turn":1,"time":0.3}
{"type":"speech_ended","turn":1,"time":12.252}
{"type":"charged","seq":1,"seconds":12.252,"credits":2.56,"total_credits":2.56}
{"type":"transcript","turn":1,"is_final":true,"text":"สวัสดีค่ะคุณสมชายยอดชำระของท่านจำนวน พันสองร้อยห้าสิบ บาทครบกำหนดวันที่ สิบห้า สิงหาคมนี้ชำระผ่านแอปได้ตลอด ยี่สิบสี่ ชั่วโมงค่ะ","start":0.3,"end":12.252}
{"type":"done","total_seconds":12.252,"turns":1,"total_credits":2.56}
```

## Before you write any code

[Transcribe](https://paxalabs.com/app/transcribe) has a live mode. It asks for the microphone, relays the audio through our server, and shows each turn as it closes with the running charge beside it. Speak into it and watch where the boundaries fall on your own Thai.

## Before you ship

- Act on finals. An interim transcript is a draft and the next one replaces it.
- Send `playback` around your agent's own audio. Without it the agent hears itself and opens a turn.
- Drop a turn you already answered. The turn number is the only ordering a caller needs.
- Declare the encoding the bytes actually carry. A wrong sample rate reaches the model as noise.
- Reconnect on `1001`. A deploy on our side should be invisible to the person on the line.
- Set a `vocabulary` for the names your callers say. Product names, Thai place names, and staff names are what it is for.
- Hold a reply until the caller has been quiet for a moment. Thai pauses at phrase breaks, and a turn can close mid-sentence.

## Next steps

- Open [Transcribe](https://paxalabs.com/app/transcribe), switch to live mode, and speak a sentence your callers would say.
- Read [where a turn ends](https://paxalabs.com/guides/realtime-thai-transcription-turns) for why Thai makes the boundary awkward, and what the interim text is good for.
- Read the [live transcription segment](https://paxalabs.com/use-cases/live-transcription) for what a call-centre workload costs.
- Read the [speech to text product page](https://paxalabs.com/speech-to-text) for this session beside the recording endpoint's own capture.

The contract itself lives in the docs.

- [Realtime transcription](https://paxalabs.com/docs/api/stt-live): Every frame field and close code
- [Transcribe recordings](https://paxalabs.com/docs/speech-to-text): The options, and what each one does
- [Rate limits and concurrency](https://paxalabs.com/docs/rate-limits): Connections a plan may hold at once
- [Credits, charges, and refunds](https://paxalabs.com/docs/credits): Periods, receipts, and refunds

# Paxa TTS Flash now speaks Mandarin Chinese, with six new voices

Paxa TTS Flash reads Mandarin Chinese from Simplified script beside Thai and English, and six new voices join the roster, two in each language.

Published 2026-09-16, updated 2026-10-01. Paxa Labs.

A Thai business with Chinese-speaking customers publishes Chinese copy beside its Thai copy. Until September 14, voicing the Chinese half took a second speech vendor or a recording session. It now goes through the same API request as the Thai copy, with a Mandarin voice.

The six new voices:

- [Tao Huay](https://paxalabs.com/voices/taohuay), Drama lead: https://paxalabs.com/audio/taohuay.m4a
- [Oolong](https://paxalabs.com/voices/oolong), Audio fiction: https://paxalabs.com/audio/oolong.m4a
- [Espresso](https://paxalabs.com/voices/espresso), Long-form reader: https://paxalabs.com/audio/espresso.m4a
- [Mocha](https://paxalabs.com/voices/mocha), Wellness: https://paxalabs.com/audio/mocha.m4a
- [Khao Niao](https://paxalabs.com/voices/khaoniao), Breathwork: https://paxalabs.com/audio/khaoniao.m4a
- [Tom Yum](https://paxalabs.com/voices/tomyum), Best friend: https://paxalabs.com/audio/tomyum.m4a

## What changed

- Paxa TTS Flash reads Mandarin Chinese beside Thai and English, from Simplified script (简体中文).
- Six voices join the roster, for 32 in all. Two are Mandarin-first, two English-first, and two Thai-first.
- `GET /v1/voices` returns `"zh"` in the `language` field for the Mandarin voices.
- The model id stays `paxa-tts-flash-v1`, and existing voice ids and requests work as before.

## Where the new voices work

Every speech surface resolves the new voice ids the way it resolves the existing ones.

- `POST /v1/tts` returns buffered or streamed audio in mp3, opus, or wav.
- `POST /v1/audio/speech`, the [OpenAI-compatible route](https://paxalabs.com/docs/openai-compatibility), takes `taohuay` or `oolong` as its `voice`.
- The [live socket](https://paxalabs.com/docs/live-streaming) reads Mandarin while the text is still arriving. There, `。`, `！`, and `？` end a sentence the way a period does, and the buffered sentence is spoken.
- `speed`, from 0.5 to 1.5, applies to the Mandarin voices as it does to every voice.

## The six voices, one by one

### Tao Huay, drama lead

[Tao Huay](https://paxalabs.com/voices/taohuay) is tender and a little melancholy. It is cast for drama recaps, audio novels, and companion speech.

Its published sample reads “如果时间可以倒流，我还是会在那个下雨的傍晚，安静地等你回来。” The line means “If time could run backward, I would still wait quietly for you to come back on that rainy evening.”

A Thai companion app that also answers in Mandarin can pair it with [Sanae Chan](https://paxalabs.com/voices/sanaechan), the Thai voice the [AI companion page](https://paxalabs.com/ai-companion) opens on.

### Oolong, audio fiction

[Oolong](https://paxalabs.com/voices/oolong) is a low, rich narrator, cast for audio novels, brand reads, and late-night storytelling.

Its sample reads “夜深了，城市渐渐安静下来。今晚的故事，从一封迟到二十年的信开始。” The line means “It is late at night, and the city is slowly growing quiet. Tonight's story begins with a letter that arrived twenty years late.”

The Thai voice for the same late hour is [Oliang](https://paxalabs.com/voices/oliang), cast for late-night radio.

### Espresso, long-form reader

[Espresso](https://paxalabs.com/voices/espresso) is deep, dark, and slow, cast for audiobooks, documentaries, and trailers. Its sample reads “Some stories are never told. They are kept, and passed from hand to hand, until someone finally dares to open the book.”

[Latte](https://paxalabs.com/voices/latte) remains the English voice cast for polished brand films. Espresso takes the long read and the trailer.

### Mocha, wellness

[Mocha](https://paxalabs.com/voices/mocha) is close, gentle, and low in volume, cast for bedtime, wellness, and companion speech. Its sample reads “Take a slow breath. There is no rush tonight. Whatever happened today, you made it here, and that is enough.”

A wellness app in Thai and English can pair it with [Lod Chong](https://paxalabs.com/voices/lodchong), cast for sleep and meditation. The [meditation page](https://paxalabs.com/meditation) plays the three calm Thai voices side by side.

### Khao Niao, breathwork

[Khao Niao](https://paxalabs.com/voices/khaoniao) is low and unhurried, cast for guided breathing, wellbeing narration, and slow documentary.

Its sample reads “หายใจเข้าช้า ๆ แล้วปล่อยออกไปครับ ไม่ต้องรีบ ทุกอย่างรอได้ ตอนนี้แค่อยู่ตรงนี้ก็พอแล้วครับ” The line means “Breathe in slowly, then let it out. There is no rush, and everything can wait. For now, being here is enough.” ครับ, the polite ending of a male speaker, appears twice.

It joins [Sangkaya](https://paxalabs.com/voices/sangkaya), the quiet close-mic voice cast for ASMR and calm narration.

### Tom Yum, best friend

[Tom Yum](https://paxalabs.com/voices/tomyum) is fast, hot, and chatty, a best-friend voice cast for drama recaps, entertainment news, and social clips.

Its sample reads “แกรู้ยัง เมื่อคืนมีเรื่องใหญ่มากเลย ฉันเพิ่งรู้มาสด ๆ ร้อน ๆ เดี๋ยวเล่าให้ฟังนะจ๊ะ” The line means “Have you heard? Something big happened last night. I only just found out. Let me tell you.”

The register lives in the words. แก is the second-person pronoun of close friends, and จ๊ะ is a playful, familiar ending. The same script written in polite Thai reads as polite in this voice too. For ads and shorts with a confident male read, [Moo Ping](https://paxalabs.com/voices/mooping) remains the voice with street energy.

## How a request reads Chinese

The model reads each run of text by its script: Chinese characters as Mandarin, Thai script as Thai, and Latin words as English. A Thai announcement and its Mandarin version differ only in `voice` and `text`. The optional `language` field changes only how codes, digits, and symbols are read, and its default, `auto`, follows the language most of the text is written in.

one endpoint, two languages:

```
POST /v1/tts

{ "model": "paxa-tts-flash-v1", "voice": "nomyen",
  "text": "ยินดีต้อนรับค่ะ กรุณาเตรียมเอกสารการจองไว้ที่จุดเช็กอินค่ะ" }

{ "model": "paxa-tts-flash-v1", "voice": "taohuay",
  "text": "欢迎光临，请在办理入住处准备好您的预订资料。" }
```

The two requests above:

- [Nom Yen](https://paxalabs.com/voices/nomyen), Lead voice: https://paxalabs.com/audio/blog/mandarin-chinese-and-six-new-voices/arrival-th.m4a
- [Tao Huay](https://paxalabs.com/voices/taohuay), Drama lead: https://paxalabs.com/audio/blog/mandarin-chinese-and-six-new-voices/arrival-zh.m4a

The voice sets the accent. A Mandarin-first voice is designed for sentences written wholly in Chinese, and it reads Thai or English with a foreign accent. A Chinese name inside a Thai sentence takes the accent of the Thai voice reading it.

## Chinese family names in Thai copy

Chinese characters are always read as Mandarin, which matters most for family names. [More than half of Thailand's Chinese community](https://en.wikipedia.org/wiki/Thai_Chinese) traces its ancestry to Chaoshan, where Teochew is spoken. The family names Thai speakers know carry those Teochew sounds, as [Sanook's list of common family names](https://www.sanook.com/campus/1431575/) shows beside the Mandarin readings.

| Characters | Family name in Thai | Mandarin reading |
| --- | --- | --- |
| 陈 | แซ่ตั้ง | Chén |
| 林 | แซ่ลิ้ม | Lín |
| 黄 | แซ่อึ้ง | Huáng |
| 李 | แซ่ลี้ | Lǐ |

Sent as characters, 林 is read Lín. In Thai copy, write the family name in Thai script, as ลิ้ม, and a Thai voice says it the way the family does. In Mandarin copy written for a Mandarin speaker, the Mandarin reading is the one the listener expects.

## What Mandarin costs

Mandarin bills at 10 credits per 1,000 characters, the [one speech rate](https://paxalabs.com/pricing) for every voice and language. A Chinese character carries a whole syllable, while Thai writes each vowel and tone mark as a character of its own. Every announcement on the [Thai and Mandarin announcements page](https://paxalabs.com/thai-chinese) takes fewer characters in Mandarin.

| Announcement | Thai | Mandarin |
| --- | --- | --- |
| Arrival | 58 characters, 0.58 credits | 22 characters, 0.22 credits |
| Directions | 36 characters, 0.36 credits | 12 characters, 0.12 credits |
| Closing time | 55 characters, 0.55 credits | 21 characters, 0.21 credits |

A new account's 100 free credits voice the Mandarin arrival announcement 454 times.

## One voice for each language

A product that speaks three languages keeps one voice per language for each job. Each line-up takes its voices from their catalog casting, or from a page that already pairs them.

| Job | Thai | English | Mandarin |
| --- | --- | --- | --- |
| Audiobooks and long reads | Foi Thong | Espresso | Oolong |
| Companion speech | Sanae Chan | Mocha | Tao Huay |
| Brand reads | Nom Yen | Latte | Oolong |
| Front desk announcements | Nom Yen | Cookie | Tao Huay |

The front desk line-up matches the [Thai and Mandarin announcements page](https://paxalabs.com/thai-chinese), where Nom Yen and Tao Huay read each announcement as one announcer in two languages.

## Before you publish

- Convert Traditional script to Simplified before sending. Only Simplified script is verified.
- Expect Mandarin for every Chinese line, including copy written for Cantonese speakers.
- Have a Mandarin speaker write or check the copy. The voice reads the characters it receives, mistakes included.
- Play a line that mixes Thai and Chinese in the Speech tool before customers hear it.

## Try Mandarin

The [free text to speech page](https://paxalabs.com/tts) reads a line of up to 50 characters in Tao Huay or Oolong, without an account. Signed in, the [Speech tool](https://paxalabs.com/app/speech?voice=taohuay) takes up to 5,000 characters a request.

The [Mandarin voices page](https://paxalabs.com/chinese-voices) plays both voices on one welcome line. The [Chinese listening page](https://paxalabs.com/chinese-learning) reads four lesson lines with their pinyin, and the [languages page](https://paxalabs.com/languages) lists every page by language.
