
Research
/Security News
737 Chrome VPN Extensions Linked to Brand Impersonation and Browser Traffic Redirection
The campaign amassed more than 75,000 installs by targeting Russian-speaking users seeking access to blocked services.
kitten-tts-js
Advanced tools
Unofficial JavaScript/TypeScript port of KittenTTS (by KittenML/Stellon Labs) — ultra-lightweight TTS via ONNX models, runs in Node.js and browser
JavaScript/TypeScript port of KittenTTS — ultra-lightweight neural TTS via ONNX. Works in Node.js, browser (WebAssembly), and any JS environment. Zero Python dependency.
Live Demo → · npm → · GitHub →
Based on KittenTTS by KittenML / Stellon Labs — original Python library: github.com/KittenML/KittenTTS — original models & voices: huggingface.co/KittenML
All credit for the models, architecture, and voice embeddings goes to them. Licensed under Apache 2.0. See NOTICE for full attribution.
Disclaimer: This is an unofficial community port made by a hobbyist who needed KittenTTS in JavaScript. It is not affiliated with, endorsed by, or supported by KittenML or Stellon Labs.
~/.cache/kitten-tts/ in Node, Cache API in browsernpm install kitten-tts-js
import { KittenTTS } from 'kitten-tts-js';
const tts = await KittenTTS.from_pretrained('KittenML/kitten-tts-nano-0.8');
console.log(tts.list_voices());
// → ['Bella', 'Jasper', 'Luna', 'Bruno', 'Rosie', 'Hugo', 'Kiki', 'Leo']
const audio = await tts.generate('Hello from KittenTTS!', { voice: 'Bella' });
await audio.save('output.wav');
<script type="module">
import { KittenTTS } from 'https://esm.sh/kitten-tts-js';
const tts = await KittenTTS.from_pretrained('KittenML/kitten-tts-nano-0.8');
const audio = await tts.generate('Hello!', { voice: 'Luna' });
const audioCtx = new AudioContext();
const source = audioCtx.createBufferSource();
source.buffer = audio.toAudioBuffer(audioCtx);
source.connect(audioCtx.destination);
source.start();
</script>
Running inference in a Worker keeps the UI thread responsive during the ~5–10 s model load and synthesis.
worker.js
import { KittenTTS } from 'https://esm.sh/kitten-tts-js';
let tts;
self.onmessage = async ({ data }) => {
if (data.type === 'load') {
tts = await KittenTTS.from_pretrained(data.modelId);
self.postMessage({ type: 'ready' });
}
if (data.type === 'generate') {
const audio = await tts.generate(data.text, data.opts);
const buf = new Float32Array(audio.data);
self.postMessage({ type: 'audio', buf, sampleRate: audio.sampling_rate }, [buf.buffer]);
}
};
main.js
const worker = new Worker('./worker.js', { type: 'module' });
worker.postMessage({ type: 'load', modelId: 'KittenML/kitten-tts-nano-0.8' });
worker.onmessage = ({ data }) => {
if (data.type === 'ready') console.log('Model loaded!');
if (data.type === 'audio') playFloat32(data.buf, data.sampleRate);
};
worker.postMessage({ type: 'generate', text: 'Hello world!', opts: { voice: 'Bella' } });
function playFloat32(buf, sampleRate) {
const audioCtx = new AudioContext({ sampleRate });
const ab = audioCtx.createBuffer(1, buf.length, sampleRate);
ab.copyToChannel(buf, 0);
const src = audioCtx.createBufferSource();
src.buffer = ab;
src.connect(audioCtx.destination);
src.start();
}
let i = 0;
for await (const { text, audio } of tts.stream(longText, { voice: 'Leo' })) {
console.log(`Chunk: "${text}" → ${audio.duration.toFixed(1)}s`);
await audio.save(`chunk-${i++}.wav`);
}
KittenTTS.from_pretrained(modelId?, opts?)| Param | Type | Default | Description |
|---|---|---|---|
modelId | string | 'KittenML/kitten-tts-nano-0.8' | HuggingFace repo ID |
opts.cacheDir | string | ~/.cache/kitten-tts | Override cache dir (Node) |
tts.generate(text, opts?)Returns Promise<RawAudio>.
| Opt | Default | Description |
|---|---|---|
voice | 'Leo' | Voice name (see table below) |
speed | 1.0 | Speed multiplier (0.5–2.0) |
clean | true | Run text preprocessor (numbers, currency, etc.) |
tts.stream(text, opts?)Returns AsyncGenerator<{ text: string, audio: RawAudio }> — one chunk per sentence.
tts.list_voices()Returns string[] of available friendly voice names.
tts.release()Releases the underlying ONNX session to free WebAssembly memory. Useful when switching models in the browser.
RawAudio| Member | Description |
|---|---|
.data | Float32Array — raw PCM mono |
.sampling_rate | 24000 |
.duration | Duration in seconds |
.toWav() | ArrayBuffer — 16-bit PCM WAV |
.save(path) | Write WAV file (Node.js) |
.toBlob() | Blob for browser download/playback |
.toAudioBuffer(ctx) | Web Audio AudioBuffer |
| Model ID | Size | Speed | Quality |
|---|---|---|---|
KittenML/kitten-tts-nano-0.8 | ~25 MB | ★★★ | ★★☆ |
KittenML/kitten-tts-micro-0.8 | ~40 MB | ★★☆ | ★★★ |
KittenML/kitten-tts-mini-0.8 | ~80 MB | ★☆☆ | ★★★ |
| Friendly Name | Gender |
|---|---|
| Bella | Female |
| Jasper | Male |
| Luna | Female |
| Bruno | Male |
| Rosie | Female |
| Hugo | Male |
| Kiki | Female |
| Leo | Male |
git clone https://github.com/Algiras/kitten-tts-js.git
cd kitten-tts-js
npm install
npm test # run unit tests
npm run build:pages # build browser bundle → docs/
src/
├── kitten-tts.js Main class: from_pretrained, generate, stream
├── preprocess.js Number/currency/time text normalization
├── text-cleaner.js Phoneme → token IDs (IPA symbol table)
├── phonemizer.js eSpeak-NG WASM phonemization
├── npz-loader.js NumPy .npz binary parser
├── model-loader.js HuggingFace Hub download + caching
├── audio.js RawAudio class + WAV encoder
└── index.js Public API re-exports
Apache 2.0 — see NOTICE for attribution to the original KittenTTS by KittenML / Stellon Labs.
FAQs
Unofficial JavaScript/TypeScript port of KittenTTS (by KittenML/Stellon Labs) — ultra-lightweight TTS via ONNX models, runs in Node.js and browser
We found that kitten-tts-js demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
The campaign amassed more than 75,000 installs by targeting Russian-speaking users seeking access to blocked services.

Company News
Open source maintainers are under more pressure than ever. We're raising our open source program from the Team plan to the Business plan, free.

Security News
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.