npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

pocket-tts

v1.0.0

Published

Streaming Pocket TTS for Node.js — cloud mode (Kyutai API, 24 built-in voices) and local mode (ONNX runtime with zero-shot voice cloning).

Readme

pocket-tts

Streaming Pocket TTS for Node.js — Kyutai's 100M-parameter TTS with zero-shot voice cloning, in two modes:

  • cloud — the Kyutai API: 24 built-in voices, 7 languages, audio streamed from the first byte (~60 ms TTFB). No cloning (the API has no upload endpoint).
  • local — the community ONNX export: offline, clone any voice from 1–30 s of reference audio (no training, ~3–8 s one-shot on a laptop CPU), 10 language bundles.

Both modes share one interface: stream() yields audio chunks as they're produced, synthesize() returns a complete WAV.

Install

npm install pocket-tts

Node.js ≥ 18.17 (cloud mode uses the global fetch). Local mode additionally needs onnxruntime-node (installed automatically as an optional peer):

npm install onnxruntime-node   # local mode only

Quick start

Cloud mode (built-in voices, streaming)

import { PocketTTS } from "pocket-tts";

const tts = await PocketTTS.load({
  mode: "cloud",
  language: "french",
  voice: "estelle",
});

// Streamed: chunks arrive while the audio is still being generated
for await (const chunk of tts.stream("Bonjour tout le monde.")) {
  player.enqueue(chunk.pcm); // Float32Array @ 24 kHz
}
tts.close();

Local mode (zero-shot voice cloning)

import { PocketTTS } from "pocket-tts";
import * as fs from "node:fs";

const tts = await PocketTTS.load({
  mode: "local",
  language: "french_24l",
  voice: "./my-voice.wav", // any clean WAV — cloned without training
});

const result = await tts.synthesize("Cette phrase sort de ma voix clonée.");
await fs.writeFile("cloned.wav", result.wav);
tts.close();

The model bundle (~90–360 MB) is downloaded once into the cache directory on first use, then reused.

Voices

Cloud mode accepts only the 24 built-in voices (the API has no cloning endpoint):

import { listCloudVoices } from "pocket-tts";

for (const voice of listCloudVoices()) {
  console.log(`${voice.id}: ${voice.name} (${voice.gender}, ${voice.style})`);
}
// jane: Jane (female, conversation)
// estelle: Estelle (female, conversation)  ← French
// …24 total

Languages: english, french, german, spanish, portuguese, italian, dutch. Voices are language-agnostic — estelle works with the English model too (the voice provides the timbre, the language the model).

Requesting a clone (./voice.wav, a URL, …) in cloud mode throws CloudModeCloningError — cloning requires local mode.

Local bundles

import { listLocalLanguages } from "pocket-tts";

console.log(listLocalLanguages());
// english_2026-04 (distilled), french_24l (24-layer), german, german_24l, …

The *_24l bundles are the larger non-distilled preview models (higher quality, slower); plain names are the 6-layer distilled models that reach real time on modern laptops. Measured on an i5-7500 (2017 desktop CPU): English 6-layer ≈ 0.8× real time, French 24-layer ≈ 0.5×. On a recent laptop CPU or with a GPU both are comfortably faster.

API

PocketTTS.load(options)

| Option | Mode | Type | Default | Notes | |---|---|---|---|---| | mode | both | "cloud" \| "local" | required | Runtime selection | | language | cloud | CloudLanguage | "english" | 7 languages | | language | local | LocalLanguage | "english_2026-04" | 10 bundles | | voice | cloud | string | language default | Built-in voice id only | | voice | local | string \| Float32Array \| { pcm, sampleRate } | required | WAV path or raw samples to clone | | endpoint | cloud | string | Kyutai demo API | Point at your own pocket-tts serve | | timeoutMs | cloud | number | 120000 | Overall request timeout | | cacheDir | local | string | platform cache dir | Bundle download cache | | numThreads | local | number | ORT default | Intra-op threads | | lowMemory | local | boolean | false | Disables ORT memory arena (lower peak RAM, small speed loss) | | temperature | local | number | 0.7 | Lower = more stable | | lsdSteps | local | number | 1 | Flow sampler steps; 5 + temperature: 0.5 = highest quality | | fetchImpl | both | typeof fetch | global fetch | Tests, proxies, mirrors |

tts.stream(text)

Async generator of AudioChunk { pcm: Float32Array, sampleRate, isFirst, isLast } at 24 kHz. Cloud mode streams from the HTTP response (TTFB ~60 ms on the public API); local mode emits the first chunk after ~240 ms of generated audio (3 frames), then every ~960 ms.

tts.synthesize(text)

Returns SynthesisResult { wav: Buffer, pcm: Float32Array, sampleRate, durationSec, mode, language }. On cloud mode the WAV is what the server streamed; on local mode it's assembled from the decoded chunks.

tts.close()

Aborts in-flight work and releases ONNX sessions. Idempotent.

Errors

Every error extends PocketTTSError with a stable code:

| Error | Code | When | |---|---|---| | CloudModeCloningError | CLOUD_MODE_NO_CLONING | Cloning requested in cloud mode | | UnknownVoiceError | UNKNOWN_VOICE | Cloud voice id not in the catalog (error.available lists them) | | UnknownLanguageError | UNKNOWN_LANGUAGE | Invalid language code | | BundleDownloadError | BUNDLE_DOWNLOAD_FAILED | Local bundle download failed | | AudioDecodeError | AUDIO_DECODE_FAILED | Reference audio undecodable | | RequestFailedError | REQUEST_FAILED | Cloud HTTP/network/timeout failure (error.status) | | InvalidOptionError | INVALID_OPTION | Inconsistent options (e.g. no voice prepared) |

Self-hosting the cloud mode

The default endpoint is Kyutai's public demo API (no SLA — be reasonable). To self-host, run the official server and point the client at it:

uvx pocket-tts serve --language french --default-voice ./my-voice.wav
const tts = await PocketTTS.load({
  mode: "cloud",
  endpoint: "http://localhost:8000",
  // voice: whatever your server exposes
});

The client targets the POST /{language}/tts multipart shape (text + voice_url) used by the Kyutai deployment. Community OpenAI-compatible servers (/v1/audio/speech) speak a different protocol and are not directly compatible.

How cloning works (local mode)

Zero-shot, no training: reference audio → Mimi encoder → speaker embedding → one prefill pass conditions the flow-LM's KV caches. The prepared state is cached for the instance, so cloning cost (~3–8 s on a laptop CPU, dominated by encoding the reference) is paid once; switching voices re-runs the prefill only.

Licenses

Cloud mode sends your text to Kyutai's API; use local mode for privacy-sensitive content.

Demo (real-time server)

A Hono-based demo server with a browser UI lives in demo/ — audio is streamed as binary PCM and played gaplessly via an AudioWorklet while it is still being generated:

npm run demo                                   # cloud mode only
POCKET_VOICE=./my-voice.wav npm run demo       # + local cloning engine

Then open http://localhost:3000. Endpoints: POST /api/tts/stream (chunked PCM16LE @24kHz), POST /api/tts/wav, GET /api/config, POST /api/local/prepare. Measured TTFB: ~78 ms (cloud).