AIniverse Get the App

sanoTTS

***sano*** (सानो) — Nepali for **"small."** A family of tiny neural text-to-speech voices — **294k to 2.27M parameters** — that run with **no cloud and no NPU**: real-time on a ~$3 ESP32-S3 (out a GPIO into an LM386 and a speaker), or live in the browser via WASM. **Live demo:** [tts.ampixa.com/sanoTTS](https://tts.ampixa.com/sanoTTS) — every voice synthesizes your text live in the browser, no server, no upload. Both packages stream their weights from **this repo** by default. import sanotts sanotts.synthesize("Hello world", voice="heart") # downloads heart/ from here import { SanoTTS } from 'sanotts-web'; // fetches web/voices/ from here const tts = await SanoTTS.load(); const result = await tts.synthesize('Hello world', { voice: 'heart' }); Python needs `sanotts` >= 0.3.0, the browser `sanotts-web` >= 0.3.0. Both fall back to the GitHub releases or the Pages host if Hugging Face cannot be reached, so an outage here cannot break an install. Python packages land in `~/.cache/sanotts/`; set `SANOTTS_VOICE_SOURCE=hf` or `=github` to pin one host. In the browser, passing `voiceBase` yourself turns the fallback off, so a self-hosted deployment never quietly reaches back to our servers. Two clips per voice are in this repo's `samples/` folder; one is embedded below. "Package here" means this repo carries that voice's weights. The three languages without one currently ship only through the [browser demo](https://tts.ampixa.com/sanoTTS) and the GitHub repo's `web/voices/` — their packages haven't been exported yet. SCOREQ is a no-reference naturalness predictor, higher is better; it is only reported for the English voices, which share one 24-sentence eval set. The other languages haven't been scored against a comparable reference yet. **Size does not order this table, and we are not going to pretend it does.** `amy` at 1.46M scores higher than `heart` at 2.27M because they come from different teachers and different architectures, not because one is bigger. `amy` and the voices above it are the **piperlite** lineage, distilled from a Piper/VITS teacher at 22.05 kHz. `heart` and `heart-nano` are the newer **nano** lineage — 24 kHz, mel-100 → ConvNeXt1D → iSTFT, distilled from a Kokoro teacher through a frozen Vocos — which is the one that quantizes to int8 and runs on a microcontroller. The two are not interchangeable and the `sanotts` package picks the right runtime for you. The `heart` and `heart-nano` scores were re-measured on 2026-09-04; the eval set, checkpoint hashes, exact commands and all 24 per-clip scores are in [`evidence/heart-diverse24-remeasure-20260904.json`](https://huggingface.co/ampixa/sanoTTS/blob/main/evidence/heart-diverse24-remeasure-20260904.json). They were scored on the float32 reference render. The shipped `heart-nano` int8 export tracks that render at 0.981 waveform correlation, and `heart` ships as float32 because its int8 export reached only 0.951 against a 0.98 gate. The "robot" row is the 567,008-parameter model that runs on the ESP32-S3. Pip voices: `heart`, `hfc`, `amy-1p8m`, `amy`, `kristin`, `vi`, `id`, `amy-1p1m`, `heart-nano`. Pure numpy inference, no torch, no onnxruntime. Open small-scale TTS on an honest gate — a diverse 24-sentence set scored with the **same** no-reference suite (SCOREQ / UTMOS are naturalness predictors, DNSMOS-SIG is signal quality; higher is better). Parameter counts are inference-time and exclude the shared external G2P. sanoTTS is the **smallest** model here and the **best on naturalness (SCOREQ and UTMOS) among everything up to 15M params** — beating TinyTTS while being smaller. On DNSMOS-SIG, TinyTTS edges us by 0.01 — no single metric tells the whole story. It's the only one that runs a full neural stack on a $3 MCU. The frontier only pulls ahead at Piper, the ~15M teacher we distilled from, and Kokoro (82M, 56× larger) — a gap we don't claim to close. Reproduce it with `tools/eval_mos_all.py` + `tools/eval_scorecard.py` in the GitHub repo. Shipped-file sizes: sanoTTS amy 2.8 MB fp16 and TinyTTS 3.5 MB fp16, both verified from the released files; Kokoro's ~330 MB fp32 is its widely cited public figure. espeak-ng provides phoneme IDs; a duration model predicts timing; an acoustic model predicts generator latents; a decoder renders audio. The piperlite voices (amy, kristin, hfc, and the other languages) use a compact time-domain decoder running in fp32 at 22.05 kHz. The nano voices (heart, heart-nano) and the 567,008-parameter on-device model use an iSTFT decoder at 24 kHz, quantized to int8 where it has to fit and run in real time on the ESP32-S3. Models are distilled from a Piper/VITS or Kokoro teacher — see [`docs/distillation-recipe.md`](https://github.com/Ampixa/sanoTTS/blob/master/docs/distillation-recipe.md) in the GitHub repo for the full recipe. - **ESP32-S3 talking device** — a standalone WiFi dashboard: type text, the board phonemizes (on-chip espeak-ng) and speaks. See [`mcu/ports/esp32s3/`](https://github.com/Ampixa/sanoTTS/tree/master/mcu/ports/esp32s3). Board-by-board measurements are in [`BOARDS.md`](https://github.com/Ampixa/sanoTTS/blob/master/BOARDS.md); the silicon figures there are for the `en_us_e12nano` lineage, which is a sibling of `heart-nano`, not the same weights. - **Browser** — the full stack in WASM, no server. **[▶ Hear and synthesize all 11 voices live](https://tts.ampixa.com/sanoTTS)**; source in [`web/`](https://github.com/Ampixa/sanoTTS/tree/master/web). - **Other MCUs** — which chips can run it and how well: [`docs/mcu-classes-and-porting.md`](https://github.com/Ampixa/sanoTTS/blob/master/docs/mcu-classes-and-porting.md). - Source, recipes, eval tooling: [github.com/Ampixa/sanoTTS](https://github.com/Ampixa/sanoTTS) - Live browser demo: [tts.ampixa.com/sanoTTS](https://tts.ampixa.com/sanoTTS) - npm package: [sanotts-web](https://www.npmjs.com/package/sanotts-web) - PyPI package: [sanotts](https://pypi.org/project/sanotts/) GPLv3 — see [`LICENSE`](https://github.com/Ampixa/sanoTTS/blob/master/LICENSE). The pipeline builds on GPLv3 components (notably [espeak-ng](https://github.com/espeak-ng/espeak-ng) for G2P, and [piper](https://github.com/OHF-Voice/piper1-gpl)), so the project as a whole is GPLv3. Copyright (C) 2026 Ampixa. Two package layouts, because there are two graphs. **piperlite** (`amy-en-1p46m/`, `kristin-en-1p4m/`, `hfc-en-1p8m/`, `amy-en-1p1m/`, `id-newstts-1p46m/`, `vi-vais1000-1p46m/`) — a flat fp16 blob addressed by manifest offsets: manifest.json weights.fp16.bin piper-phoneme-config.json (+ sibilant-injection calibration where applicable) **nano** (`heart/`, `heartnano/`) — the mel-100 stack, two blobs plus the generated offset header: meta.json lineage, per-file sha256, sample rate, vocab front_*.bin duration + acoustic model_*.bin decoder nano_q8_meta.h tensor offsets `heartnano/` ships `*_q8.bin` (int8, 345,232 bytes total); `heart/` ships `*_f32.bin` (float32, 9,137,920 bytes) because its int8 export missed the fidelity gate. Both layouts are consumed by the `sanotts` Python package and the portable C runtime. **`web/voices/`** is a third thing and exists for the browser. The piperlite voices ship there as `front_f32.bin` + `dec_f32.bin`, a different artifact from the `weights.fp16.bin` the Python package reads, which is why the same voice appears twice in this repo under two names. It mirrors `web/` in the GitHub repo byte for byte and is what `sanotts-web` fetches. The nano voices are not duplicated: `web/voices/heart/` and `heart/` hold the same blobs. `samples/` holds the audio clips embedded above (mp3, one or two per voice, plus the on-device `mcu-567k.mp3`), and `evidence/` the eval report behind the heart scores.

Modality
text->audio
License
gpl-3.0
Open source
Yes

Open sanoTTS in AIniverse

Compare versions, read real ratings, save to your stack.

Open in App