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

# Javascript/Typescript

> Access the Typecast API with our official Javascript/Typescript SDK.

The official Node.js library for the [Typecast API](https://typecast.ai). Convert text to lifelike speech using AI-powered voices.

Works with both Javascript and TypeScript. Full TypeScript types included.

ESM & CommonJS supported. Works in Node.js 18+ and modern browsers. Node.js 16/17 users need to install `isomorphic-fetch` polyfill.

<CardGroup cols={2}>
  <Card title="Package" icon="npm" href="https://www.npmjs.com/package/@neosapience/typecast-js">
    Typecast Javascript/Typescript SDK
  </Card>

  <Card title="Source Code" icon="github" href="https://github.com/neosapience/typecast-sdk/tree/main/typecast-js">
    Typecast Javascript/Typescript SDK Source Code
  </Card>
</CardGroup>

## Installation

<Tabs>
  <Tab title="npm">
    ```bash theme={null}
    npm install @neosapience/typecast-js@latest
    ```
  </Tab>

  <Tab title="pnpm">
    ```bash theme={null}
    pnpm add @neosapience/typecast-js@latest
    ```
  </Tab>

  <Tab title="yarn">
    ```bash theme={null}
    yarn add @neosapience/typecast-js@latest
    ```
  </Tab>
</Tabs>

<Warning>
  Latest registered version: **0.4.7** on npm. Make sure you have **version 0.4.7 or higher** installed. You can check your version with `npm list @neosapience/typecast-js`. If you have an older version, run `npm update @neosapience/typecast-js` to update.
</Warning>

## Quick Start

<Tabs>
  <Tab title="TypeScript (ESM)">
    ```typescript theme={null}
    import { TypecastClient } from '@neosapience/typecast-js';
    import fs from 'fs';

    async function main() {
      const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' });
      const audio = await client.textToSpeech({
        text: "Hello there! I'm your friendly text-to-speech agent.",
        model: "ssfm-v30",
        voice_id: "tc_672c5f5ce59fac2a48faeaee"
      });
      await fs.promises.writeFile(`output.${audio.format}`, Buffer.from(audio.audioData));
      console.log(`Audio saved! Duration: ${audio.duration}s, Format: ${audio.format}`);
    }

    main();
    ```
  </Tab>

  <Tab title="Javascript (CommonJS)">
    ```javascript theme={null}
    const { TypecastClient } = require('@neosapience/typecast-js');
    const fs = require('fs');

    async function main() {
      const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' });
      const audio = await client.textToSpeech({
        text: "Hello there! I'm your friendly text-to-speech agent.",
        model: "ssfm-v30",
        voice_id: "tc_672c5f5ce59fac2a48faeaee"
      });
      await fs.promises.writeFile(`output.${audio.format}`, Buffer.from(audio.audioData));
      console.log(`Audio saved! Duration: ${audio.duration}s, Format: ${audio.format}`);
    }

    main();
    ```
  </Tab>
</Tabs>

## Features

The Typecast Javascript/TypeScript SDK provides powerful features for text-to-speech conversion:

* **Multiple Voice Models**: Support for `ssfm-v30` (latest) and `ssfm-v21` AI voice models
* **Multi-language Support**: 37 languages including English, Korean, Spanish, Japanese, Chinese, and more
* **Emotion Control**: Preset emotions (normal, happy, sad, angry, whisper, toneup, tonedown) or smart context-aware inference
* **Audio Customization**: Control loudness (LUFS -70 to 0), pitch (-12 to +12 semitones), tempo (0.5x to 2.0x), and format (WAV/MP3)
* **Voice Discovery**: V2 Voices API with filtering by model, gender, age, and use cases
* **Instant Voice Cloning**: Upload a WAV/MP3 sample and create a custom voice ID
* **TypeScript Support**: Full type definitions included
* **Timestamp TTS**: Word- and character-level alignment data for subtitles, karaoke, and lip-sync
* **Zero Dependencies**: Uses native fetch API (works in Node.js 18+ and browsers)
* **Streaming**: Real-time chunked audio delivery for low-latency playback

## Voice Recommendations

Use `recommendVoices` when you know the desired style but not the exact `voice_id`.

```typescript theme={null}
const recommendations = await client.recommendVoices(
  'warm female voice for a product tutorial',
  3
);

for (const voice of recommendations) {
  console.log(voice.voice_id, voice.voice_name, voice.score);
}
```

Recommendation results contain only `voice_id`, `voice_name`, and `score`. Use `getVoiceV2(voiceId)` or `getVoicesV2()` when you need detailed metadata such as supported models, emotions, gender, age, or use cases.

## Configuration

Set your API key via environment variable or constructor:

```typescript theme={null}
// Using environment variable
// export TYPECAST_API_KEY="your-api-key-here"
const client = new TypecastClient({
  apiKey: process.env.TYPECAST_API_KEY!
});

// Or pass directly
const client = new TypecastClient({
  apiKey: 'your-api-key-here'
});
```

<Info>
  When requests go through your own proxy, set `baseHost` to the proxy endpoint and omit `apiKey`. The SDK will not send the `X-API-KEY` header for empty or missing keys.
</Info>

```typescript Proxy without API key theme={null}
const client = new TypecastClient({
  baseHost: 'https://your-proxy.example.com'
});
```

## Advanced Usage

### Emotion Control (ssfm-v30)

ssfm-v30 offers two emotion control modes: **Preset** and **Smart**.

<Tabs>
  <Tab title="Smart Mode">
    Let the AI infer emotion from context:

    ```typescript theme={null}
    import { SmartPrompt } from '@neosapience/typecast-js';

    const audio = await client.textToSpeech({
      text: "Everything is going to be okay.",
      voice_id: "tc_672c5f5ce59fac2a48faeaee",
      model: "ssfm-v30",
      prompt: {
        emotion_type: "smart",
        previous_text: "I just got the best news!",  // Optional context
        next_text: "I can't wait to celebrate!"      // Optional context
      } as SmartPrompt
    });
    ```
  </Tab>

  <Tab title="Preset Mode">
    Explicitly set emotion with preset values:

    ```typescript theme={null}
    import { PresetPrompt } from '@neosapience/typecast-js';

    const audio = await client.textToSpeech({
      text: "I am so excited to show you these features!",
      voice_id: "tc_672c5f5ce59fac2a48faeaee",
      model: "ssfm-v30",
      prompt: {
        emotion_type: "preset",
        emotion_preset: "happy",    // normal, happy, sad, angry, whisper, toneup, tonedown
        emotion_intensity: 1.5      // Range: 0.0 to 2.0
      } as PresetPrompt
    });
    ```
  </Tab>
</Tabs>

### Audio Customization

Control loudness, pitch, tempo, and output format:

```javascript theme={null}
const response = await client.textToSpeech({
  text: "Customized audio output!",
  model: "ssfm-v30",
  voice_id: "tc_672c5f5ce59fac2a48faeaee",
  output: {
    target_lufs: -14.0,   // Range: -70 to 0 (LUFS)
    audio_pitch: 2,       // Range: -12 to +12 semitones
    audio_tempo: 1.2,     // Range: 0.5x to 2.0x
    audio_format: "mp3"   // Options: wav, mp3
  }
});
```

### Generate audio to a file

Use `generateToFile` when you want the SDK to synthesize speech and write the audio bytes directly to a local file. The model defaults to `ssfm-v30`, and `.mp3` / `.wav` extensions infer the output format when `output.audio_format` is not set. Browse available voice IDs on the [Voices](https://typecast.ai/developers/api/voices) page.

```typescript theme={null}
await client.generateToFile('output.mp3', {
  text: 'Hello from Typecast.',
  voice_id: 'tc_672c5f5ce59fac2a48faeaee' // Find voice IDs at https://typecast.ai/developers/api/voices
});
```

### Text pauses

Use text pause markup when you only need silent gaps inside one composed text segment. Put `<|5s|>`, `<|1s|>`, `<|0.3s|>`, or `<|0.34413s|>` directly in the text. The value is interpreted as seconds and must end with `s`. This keeps the pause expression visible in plain text without adding separate pause calls.

```typescript theme={null}
const audio = await client
  .composeSpeech()
  .defaults({ voice_id: 'tc_672c5f5ce59fac2a48faeaee', model: 'ssfm-v30' })
  .say('Hello<|5s|>Nice to meet you<|1s|>Today<|2s|>how does the weather feel?')
  .generate();
```

### Multi-speaker composition

Use the composer chaining API when one output file needs different voices or per-segment options such as pitch, tempo, prompt, or seed. The composer generates each segment as WAV, trims leading/trailing silent PCM samples, and concatenates the result. If you need MP3, generate WAV first and convert it in your app or server pipeline.

```typescript theme={null}
import { TypecastClient } from '@neosapience/typecast-js';
import fs from 'fs/promises';

const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' });

const audio = await client
  .composeSpeech()
  .defaults({ voice_id: 'tc_672c5f5ce59fac2a48faeaee', model: 'ssfm-v30' })
  .say('Hello there')
  .pause(5)
  .say('Nice to meet you', { voice_id: 'tc_60e5426de8b95f1d3000d7b5', output: { audio_pitch: 2 } })
  .say('Today')
  .pause(2)
  .say('How does the weather feel?')
  .generate();

await fs.writeFile('conversation.wav', Buffer.from(audio.audioData));
```

### Voice Discovery (V2 API)

List and filter available voices with enhanced metadata:

```typescript theme={null}
// Get all voices
const voices = await client.getVoicesV2();

// Filter by criteria
const filtered = await client.getVoicesV2({
  model: 'ssfm-v30',
  gender: 'female',
  age: 'young_adult'
});

// Display voice info
voices.forEach(voice => {
  console.log(`ID: ${voice.voice_id}, Name: ${voice.voice_name}`);
  console.log(`Gender: ${voice.gender}, Age: ${voice.age}`);
  console.log(`Models: ${voice.models.map(m => m.version).join(', ')}`);
  console.log(`Use cases: ${voice.use_cases?.join(', ')}`);
});
```

### Multilingual Content

The SDK supports 37 languages with automatic language detection:

```typescript theme={null}
// Auto-detect language (recommended)
const audio = await client.textToSpeech({
  text: "こんにちは。お元気ですか。",
  voice_id: "tc_672c5f5ce59fac2a48faeaee",
  model: "ssfm-v30"
});

// Or specify language explicitly
const koreanAudio = await client.textToSpeech({
  text: "안녕하세요. 반갑습니다.",
  voice_id: "tc_672c5f5ce59fac2a48faeaee",
  model: "ssfm-v30",
  language: "kor"  // ISO 639-3 language code
});

await fs.promises.writeFile(`output.${audio.format}`, Buffer.from(audio.audioData));
```

### Streaming

Stream audio chunks in real-time for low-latency playback:

```javascript theme={null}
// Node 18+ (built-in fetch). Pipe stream to ffplay for real-time playback.
// Prerequisite: ffmpeg (brew/choco/apt install ffmpeg)
import { spawn } from "node:child_process";
import { TypecastClient } from '@neosapience/typecast-js';

const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' });

const ffplay = spawn(
    "ffplay",
    ["-autoexit", "-nodisp", "-loglevel", "error", "-i", "pipe:0"],
    { stdio: ["pipe", "ignore", "ignore"] },
);

const stream = await client.textToSpeechStream({
    text: "Stream this text as audio in real time.",
    model: "ssfm-v30",
    voice_id: "tc_672c5f5ce59fac2a48faeaee",
    output: { audio_format: "wav" }
});

// ReadableStream — read chunks as they arrive
const reader = stream.getReader();
while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    ffplay.stdin.write(value);
}
ffplay.stdin.end();
await new Promise((resolve) => ffplay.on("close", resolve));
```

<Note>
  **WAV streaming format:** 32000 Hz, 16-bit, mono PCM. The first chunk includes a 44-byte WAV header (size = `0xFFFFFFFF`); subsequent chunks are raw PCM only. For MP3: 320 kbps, 44100 Hz, each chunk is independently decodable.
</Note>

## Timestamp TTS

`textToSpeechWithTimestamps()` wraps `POST /v1/text-to-speech/with-timestamps` and returns the audio together with per-word and per-character alignment data — useful for karaoke highlights, subtitle generation, and lip-sync applications.

### Basic Usage

```typescript theme={null}
import { TypecastClient } from '@neosapience/typecast-js';
import fs from 'fs';

const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' });

const result = await client.textToSpeechWithTimestamps({
  text: "Hello. How are you?",
  model: "ssfm-v30",
  voice_id: "tc_60e5426de8b95f1d3000d7b5",
});

// Save audio
await fs.promises.writeFile("output.wav", result.audioBytes());

console.log(`Duration: ${result.audio_duration}s`);
result.words.forEach(w => {
  console.log(`  [${w.start_time.toFixed(3)}s – ${w.end_time.toFixed(3)}s] ${w.text}`);
});
```

### Granularity

Pass `granularity: "word"` (default) or `granularity: "char"` to control the alignment unit.

```typescript theme={null}
// Character-level alignment — required for Japanese / Chinese
const result = await client.textToSpeechWithTimestamps({
  text: "Hello. How are you?",
  model: "ssfm-v30",
  voice_id: "tc_60e5426de8b95f1d3000d7b5",
  granularity: "char",
});

result.characters.forEach(c => {
  console.log(`  [${c.start_time.toFixed(3)}s – ${c.end_time.toFixed(3)}s] ${c.text}`);
});
```

### Subtitle Export

The response object includes helpers that convert alignment data to SRT or WebVTT captions. Captions are split on sentence terminators (`. ? ! 。 ？ ！`) and capped at 7 seconds / 42 characters per cue (BBC/Netflix subtitle guidelines).

```typescript theme={null}
// Export SRT captions
const srt = result.toSrt();
await fs.promises.writeFile("output.srt", srt, "utf-8");

// Export WebVTT captions
const vtt = result.toVtt();
await fs.promises.writeFile("output.vtt", vtt, "utf-8");
```

### Save Audio Helper

```typescript theme={null}
await result.saveAudio("output.wav");
```

<Note>
  **Japanese / Chinese:** Word-level segmentation is not meaningful for languages without whitespace delimiters (jpn, zho). Use `granularity: "char"` for these languages to get character-level alignment.
</Note>

## Instant Voice Cloning

Clone a custom voice from a short audio sample, then pass the returned `uc_` voice ID directly to TTS.

```typescript theme={null}
import { TypecastClient } from '@neosapience/typecast-js';
import fs from 'node:fs/promises';

const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' });

const voice = await client.cloneVoice({
  audio: './sample.wav',
  name: 'my-voice',
  model: 'ssfm-v30',
});

const audio = await client.textToSpeech({
  text: 'Hello from my cloned voice!',
  voice_id: voice.voiceId,
  model: 'ssfm-v30',
});

await fs.writeFile('output.wav', new Uint8Array(audio.audioData));
await client.deleteVoice(voice.voiceId);
```

<Warning>
  Voice cloning audio must be **25 MB or smaller**, the audio duration must be **5-150 seconds**, and the custom voice name must be **1-30 characters**.
</Warning>

## Supported Languages

The SDK supports 37 languages with automatic language detection:

| Code  | Language  | Code  | Language  | Code  | Language   |
| ----- | --------- | ----- | --------- | ----- | ---------- |
| `eng` | English   | `jpn` | Japanese  | `ukr` | Ukrainian  |
| `kor` | Korean    | `ell` | Greek     | `ind` | Indonesian |
| `spa` | Spanish   | `tam` | Tamil     | `dan` | Danish     |
| `deu` | German    | `tgl` | Tagalog   | `swe` | Swedish    |
| `fra` | French    | `fin` | Finnish   | `msa` | Malay      |
| `ita` | Italian   | `zho` | Chinese   | `ces` | Czech      |
| `pol` | Polish    | `slk` | Slovak    | `por` | Portuguese |
| `nld` | Dutch     | `ara` | Arabic    | `bul` | Bulgarian  |
| `rus` | Russian   | `hrv` | Croatian  | `ron` | Romanian   |
| `ben` | Bengali   | `hin` | Hindi     | `hun` | Hungarian  |
| `nan` | Hokkien   | `nor` | Norwegian | `pan` | Punjabi    |
| `tha` | Thai      | `tur` | Turkish   | `vie` | Vietnamese |
| `yue` | Cantonese |       |           |       |            |

<Info>
  If not specified, the language will be automatically detected from the input text.
</Info>

## Error Handling

The SDK provides `TypecastAPIError` for handling API errors:

```typescript theme={null}
import { TypecastClient, TypecastAPIError } from '@neosapience/typecast-js';

try {
  const audio = await client.textToSpeech({
    text: "Hello world",
    voice_id: "tc_672c5f5ce59fac2a48faeaee",
    model: "ssfm-v30"
  });
} catch (error) {
  if (error instanceof TypecastAPIError) {
    // TypecastAPIError exposes: statusCode, message, response
    switch (error.statusCode) {
      case 401:
        console.error('Invalid API key');
        break;
      case 402:
        console.error('Insufficient credits');
        break;
      case 422:
        console.error('Validation error:', error.response);
        break;
      case 429:
        console.error('Rate limit exceeded - please retry later');
        break;
      default:
        console.error(`API error (${error.statusCode}):`, error.message);
    }
  } else {
    console.error('Unexpected error:', error);
  }
}
```

## TypeScript Support

This SDK is written in TypeScript and provides full type definitions:

```typescript theme={null}
import type {
  TTSRequest,
  TTSResponse,
  TTSModel,
  LanguageCode,
  Prompt,
  PresetPrompt,
  SmartPrompt,
  Output,
  VoiceV2Response,
  VoicesV2Filter
} from '@neosapience/typecast-js';
```
