All use cases

Thai AI companions and roleplay

Give a Thai AI companion or roleplay character a voice: one WebSocket per conversation, a cancel frame for interruptions, and a fixed voice id that keeps a character sounding like itself.

Ideas to try

Open in the playground
Open in the playground
Open in the playground
Open in the playground

Every card plays a real clip generated from its exact script with Nom Yen. Open one in the playground to make it say anything.

How a companion learns to speak

A companion is a conversation with no task at the end of it. That changes what the speech layer has to do. A support bot speaks a handful of scripted outcomes; a companion speaks whatever the model writes, all evening, in a voice the user has decided belongs to someone. Ask a person why they stopped opening one and the answer is rarely about the writing. It is that the voice went slightly wrong one day, or that it kept talking while they were trying to say something. Two things carry that weight. The first is a voice that never drifts, which a fixed catalog id gives you for free. The second is interruption. A person talking to a companion talks over it, and a companion that keeps reading a paragraph nobody is listening to stops feeling like a person immediately. Thailand is a chat-first market, where LINE reaches 56 million monthly active users, 78.2 percent of the population, and a Thai companion is usually a chat product that learned to speak.

Sources:DataReportal (2026)(opens in a new tab)

  1. Open a socket when the conversation opensOne WebSocket carries the whole session. Send model, voice, and format once at the start, and every turn after that is a text frame on a connection that is already warm.
  2. Flush a sentence at a timeSend each sentence as the model finishes writing it. Audio starts coming back while the rest of the reply is still being written, and a turn interrupted halfway has only that sentence to pay for.
  3. Cancel the moment the person speaksA cancel frame stops the leg being spoken and drops the buffered remainder. The session stays open, and the next text frame starts the next turn.

One socket, many turns, one interruption

ws /v1/tts/live
-> {"type":"start","model":"paxa-tts-flash-v1","voice":"sanaechan"}
<- {"type":"started","connection":"c_8f21","voice":"sanaechan"}
-> {"type":"text","text":"คืนนี้เป็นยังไงบ้าง"}
<- {"type":"charged","seq":1,"chars":19,"credits":0.29}
<- [binary audio frames]
<- {"type":"flushed","seq":1}
-> {"type":"text","text":"เล่าให้ฟังหน่อยสิ เราอยากรู้จริง ๆ นะ"}
<- {"type":"charged","seq":2,"chars":37,"credits":0.55}
<- [binary audio frames]
   # the person starts talking
-> {"type":"cancel"}
<- {"type":"cancelled","seq":2}
-> {"type":"text","text":"ได้ ๆ ฟังอยู่"}

Leg 2 keeps the 0.55 credits its receipt reported, the delta over the connection's running total. Nothing was queued behind it, and the connection carries the next turn without reconnecting.

What a companion costs to speak

ConversationRepliesCharactersCost
One evening202,400$0.04
One active user, one month1,200144,000$2.16
1,000 active users for a monthabout $2,160

Computed at 15 credits per 1,000 characters, over a 120-character reply and 40 replies a day. Billing follows the characters actually spoken.

The life of one character

Week 1A voice is chosenYou audition the roster and pick one. That choice is a slug in your config, and it is the last time anyone thinks about it.
Month 3The register settlesYou have watched real conversations and moved the character from polite to close. That was an edit to a prompt: the pronouns and particles it writes with changed, and the voice reading them did not.
Month 9Someone is attached to itThe voice is the same one they met, because a slug returns the same voice forever. Nothing here degrades, expires, or needs to be re-recorded when a provider changes something.
Month 18You add a second characterAnother slug, another prompt. The first character is untouched, and users who only ever talk to it notice nothing at all.

Nothing on this timeline involves an emotion or style parameter, because the served model has none. What moves a character over time is the writing and the casting.

For developers

The TypeScript and Python samples open the live socket, send text as it arrives, and read the receipts and audio frames back. The options request shows the voice, model, and format a character pins once for its whole life.

// 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.
const socket = new WebSocket("wss://api.paxalabs.com/v1/tts/live", {
  headers: { Authorization: `Bearer ${process.env.PAXA_API_KEY}` },
});
socket.binaryType = "arraybuffer";

socket.onopen = () => {
  socket.send(JSON.stringify({
    type: "start",
    model: "paxa-tts-flash-v1",
    voice: "khanomkrok",
    timestamps: "word",
  }));
  // Send text whenever it becomes available, token by token if you like.
  socket.send(JSON.stringify({ type: "text", text: "สวัสดีครับ ยินดีต้อนรับสู่ Paxa Labs" }));
  socket.send(JSON.stringify({ type: "flush" }));
  socket.send(JSON.stringify({ type: "end" }));
};

socket.onmessage = (event) => {
  if (typeof event.data === "string") {
    const message = JSON.parse(event.data);
    if (message.type === "done") socket.close();
    return;
  }
  player.feed(new Uint8Array(event.data)); // binary frames are audio
};

Full API reference

Hear the voices

  • Sanae ChanFemale0:04

    Warm, magnetic voice with a late-night glow.

    ค่ำคืนนี้ ปล่อยให้เสียงของฉัน อยู่เป็นเพื่อนคุณจนกว่าจะหลับนะคะ

  • Khanom KrokMale0:03

    Calm, unhurried elder voice and the roster's male lead: documentary, heritage storytelling, and narration.

    สมัยผมยังหนุ่ม แถวนี้ยังเป็นทุ่งนา เดินไปตลาดทีหนึ่งก็ค่อนวันแล้วครับ

  • Nom YenFemale0:06

    Bright, energetic voice and the roster's female lead: promos, social clips, and everyday product speech.

    ยินดีต้อนรับสู่ Paxa Labs ค่ะ เสียงภาษาไทยที่สลับเป็น English ได้กลางประโยค อย่างเป็นธรรมชาติ

  • TakoFemale0:05

    Friendly young narrator for audiobooks, recaps, and explainers.

    ยามเช้าในกรุงเทพฯ แสงแดดอ่อน ๆ ทาบลงบนถนนที่เริ่มคึกคัก

A few of the 26 production voices, generated from the API with the published voice ids. The full roster plays on the Text-to-Speech page.

Why Thai carries this differently

Thai marks a relationship in the words themselves. The pronoun a speaker picks for themselves and for the listener, and the particle that ends the sentence, place two people relative to each other before any tone of voice is involved. Paxa TTS Flash reads all of it from the spelling, which means a character's closeness is something a prompt decides and the voice simply performs.

Common questions

Will the character sound the same in six months?
Yes. A voice is a fixed id in the catalog, and the same id returns the same voice on every request. Nothing about a character's sound depends on a sample you have to keep.
How does it stop when the user interrupts?
Send a cancel frame on the live socket. The sentence being spoken stops, the queued remainder is discarded without charge, and the same connection carries the next turn.
Can a companion sound happy or sad?
There is no parameter for it today. What decides how a Thai line lands is the pronouns and particles it is written with, which is a prompt-side choice, and the voice you cast it in.
Can I give every roleplay character its own voice?
Up to 26 of them, one per catalog voice. Voice cloning is not offered, so a cast larger than the roster has to reuse voices across characters.
What does a busy companion cost?
About $2.16 a month for a user who gets 40 spoken replies a day. Speech bills per character, so a quiet month bills less and a silent one bills nothing.

Start with free credits

Sign in with Google, GitHub, or Hugging Face and spend 100 one-time free credits on your own text.