Instructions to use ampixa/sanoTTS with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Piper
How to use ampixa/sanoTTS with Piper:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
Rebuild model card: samples table with embedded audio players, install table, links
Browse files
README.md
CHANGED
|
@@ -8,41 +8,79 @@ library_name: sanotts
|
|
| 8 |
|
| 9 |
# sanoTTS — a tiny neural voice that runs anywhere
|
| 10 |
|
| 11 |
-
***sano*** (सानो) — Nepali for **"small."** A
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
the browser via WASM.
|
| 15 |
|
| 16 |

|
| 17 |
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
client-side.
|
| 32 |
|
| 33 |
## How it stacks up
|
| 34 |
|
| 35 |
-
Open small-scale TTS on an honest gate — a diverse 24-sentence set scored
|
| 36 |
-
**same** no-reference suite (SCOREQ / UTMOS are naturalness
|
| 37 |
-
is signal quality; higher is better). Parameter
|
| 38 |
-
exclude the shared external G2P.
|
| 39 |
|
| 40 |

|
| 41 |
|
| 42 |
-
Kokoro is 45x larger than our largest voice, and 110x larger than our
|
| 43 |
-
Shipped-file sizes: sanoTTS amy 2.8 MB fp16 and TinyTTS 3.5 MB
|
| 44 |
-
verified from the released files; Kokoro's ~330 MB fp32 is its
|
| 45 |
-
public figure.
|
| 46 |
|
| 47 |
| System | Params | SCOREQ | UTMOS | DNS-SIG |
|
| 48 |
| --- | ---: | :---: | :---: | :---: |
|
|
@@ -50,104 +88,62 @@ public figure.
|
|
| 50 |
| TinyTTS | 1.62 M | 3.94 | 3.65 | **3.62** |
|
| 51 |
| Inflect Nano | 4.63 M | 3.81 | 3.65 | 3.58 |
|
| 52 |
| Kitten TTS nano | 15 M | 3.02 | 3.58 | 3.43 |
|
| 53 |
-
|
|
| 54 |
| _Kokoro_ | _82 M_ | _4.89_ | _4.52_ | _3.69_ |
|
| 55 |
|
| 56 |
-
sanoTTS is the **smallest** model here and the **best on naturalness
|
| 57 |
-
UTMOS) among everything up to 15M params** — beating TinyTTS
|
| 58 |
-
On DNSMOS-SIG, TinyTTS edges us by 0.01 — no single
|
| 59 |
-
It's the only one that runs a full neural
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
`tools/eval_mos_all.py` + `tools/eval_scorecard.py`.
|
| 64 |
-
|
| 65 |
-

|
| 66 |
-
|
| 67 |
-
Same voice (amy), same duration/acoustic recipe — only decoder size changes.
|
| 68 |
-
Quality lives in the decoder: doubling it from 1.09M to 1.84M params moves
|
| 69 |
-
SCOREQ from 3.70 to 4.16.
|
| 70 |
-
|
| 71 |
-
## The voices
|
| 72 |
-
|
| 73 |
-
| Language | Voice | Params | SCOREQ |
|
| 74 |
-
| --- | --- | ---: | :---: |
|
| 75 |
-
| English 🇺🇸 | amy | 1.46 M | 4.13 |
|
| 76 |
-
| | kristin | 1.40 M | 4.09 |
|
| 77 |
-
| | hfc | 1.83 M | 3.94 |
|
| 78 |
-
| | amy-small | 1.08 M | 3.70 |
|
| 79 |
-
| | robot (on-device, int8) | 745 k | — |
|
| 80 |
-
| Nepali नेपाली | Nepali | 1.47 M | — |
|
| 81 |
-
| Hindi हिन्दी | Hindi | 1.50 M | — |
|
| 82 |
-
| Vietnamese Tiếng Việt | Vietnamese | 1.46 M | — |
|
| 83 |
-
| Indonesian Bahasa | Indonesian | 1.46 M | — |
|
| 84 |
-
| Chinese 中文 | Chinese | 1.50 M | — |
|
| 85 |
-
|
| 86 |
-
The "robot" row is the same 745k-parameter model that runs on the ESP32-S3 —
|
| 87 |
-
bit-exact with the chip's own output. SCOREQ is only reported for the English
|
| 88 |
-
voices, which share a common eval set; the other languages haven't been scored
|
| 89 |
-
against a comparable reference yet.
|
| 90 |
|
| 91 |
## How it works
|
| 92 |
|
| 93 |

|
| 94 |
|
| 95 |
-
|
| 96 |
-
|
| 97 |
The web voices (amy, kristin, hfc, and the other languages) use a compact
|
| 98 |
-
|
| 99 |
a quantized int8 iSTFT decoder, sized to fit and run in real time on the
|
| 100 |
-
ESP32-S3. All
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
The end-to-end recipe is [`docs/distillation-recipe.md`](docs/distillation-recipe.md):
|
| 105 |
-
build a probe pack from a Piper teacher → train the duration, acoustic-latent, and
|
| 106 |
-
decoder students → joint finetune → export int8. New-language porting is
|
| 107 |
-
[`docs/roota-language-porting-recipe.md`](docs/roota-language-porting-recipe.md).
|
| 108 |
-
|
| 109 |
-
```bash
|
| 110 |
-
pip install -e .
|
| 111 |
-
# then follow docs/distillation-recipe.md against any en_US Piper voice
|
| 112 |
-
```
|
| 113 |
|
| 114 |
## Deploy
|
| 115 |
|
| 116 |
-
- **ESP32-S3 talking device** — a standalone WiFi dashboard: type text, the
|
| 117 |
-
phonemizes (on-chip espeak-ng) and speaks. See
|
| 118 |
-
[`mcu/ports/esp32s3/`](mcu/ports/esp32s3
|
| 119 |
-
- **Browser** — the full stack in WASM, no server. **[▶ Hear and synthesize
|
| 120 |
-
voices live](https://ampixa.github.io/sanoTTS/)**
|
| 121 |
-
[`web/`](
|
| 122 |
- **Other MCUs** — which chips can run it and how well:
|
| 123 |
-
[`docs/mcu-classes-and-porting.md`](docs/mcu-classes-and-porting.md).
|
| 124 |
-
|
| 125 |
-
## Verify your result
|
| 126 |
|
| 127 |
-
|
| 128 |
-
phoneme-class fidelity, and G2P parity — not just a gameable MOS score:
|
| 129 |
-
`tools/eval_scorecard.py`, `tools/eval_phoneme_class_fidelity.py`,
|
| 130 |
-
`tools/eval_g2p_parity.py`.
|
| 131 |
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
[
|
| 135 |
-
|
| 136 |
-
ports), `web/` (browser demo), `configs/` + `data/textsets/` (contracts).
|
| 137 |
|
| 138 |
## License
|
| 139 |
|
| 140 |
-
GPLv3 — see [`LICENSE`](https://github.com/Ampixa/sanoTTS/blob/master/LICENSE).
|
| 141 |
-
|
| 142 |
-
[espeak-ng](https://github.com/espeak-ng/espeak-ng)
|
|
|
|
| 143 |
whole is GPLv3.
|
| 144 |
|
| 145 |
Copyright (C) 2026 Ampixa.
|
| 146 |
|
| 147 |
-
|
| 148 |
## Files here
|
| 149 |
|
| 150 |
-
Each directory is a self-contained fp16
|
| 151 |
-
`manifest.json` + phoneme
|
| 152 |
-
by the `sanotts` Python package
|
| 153 |
-
and the
|
|
|
|
|
|
| 8 |
|
| 9 |
# sanoTTS — a tiny neural voice that runs anywhere
|
| 10 |
|
| 11 |
+
***sano*** (सानो) — Nepali for **"small."** A family of tiny neural
|
| 12 |
+
text-to-speech voices — **745k to 1.8M parameters** — that run with
|
| 13 |
+
**no cloud and no NPU**: real-time on a ~$3 ESP32-S3 (out a GPIO into an
|
| 14 |
+
LM386 and a speaker), or live in the browser via WASM.
|
| 15 |
|
| 16 |

|
| 17 |
|
| 18 |
+
| | |
|
| 19 |
+
| --- | --- |
|
| 20 |
+
| Smallest neural TTS family known | **745k – 1.8M parameters** |
|
| 21 |
+
| Runs real-time on a $3 microcontroller | ESP32-S3, out a GPIO into an LM386 |
|
| 22 |
+
| Runs in the browser | WebAssembly, no server |
|
| 23 |
+
| Per-voice footprint | under **4 MB**, zero dependencies (espeak-ng phonemizer included) |
|
| 24 |
+
| Coverage | **9 voices** across **6 languages** — English, Nepali (नेपाली), Hindi (हिन्दी), Vietnamese (Tiếng Việt), Indonesian (Bahasa), Chinese (中文) |
|
| 25 |
+
| License | open source, **GPL-3.0** |
|
| 26 |
+
|
| 27 |
+
**Live demo:** [ampixa.github.io/sanoTTS](https://ampixa.github.io/sanoTTS/) —
|
| 28 |
+
every voice synthesizes your text live in the browser, no server, no upload.
|
| 29 |
+
|
| 30 |
+
## Samples
|
| 31 |
+
|
| 32 |
+
One clip per voice below (a second clip per voice is in this repo's
|
| 33 |
+
`samples/` folder). "Package here" means this HF repo carries that voice's
|
| 34 |
+
raw fp16 weights; the three without one currently only ship through the
|
| 35 |
+
[browser demo](https://ampixa.github.io/sanoTTS/) and the GitHub repo's
|
| 36 |
+
`web/voices/` — their HF packages haven't been exported yet.
|
| 37 |
+
|
| 38 |
+
| Voice | Language | Params | SCOREQ | Package here | Sample |
|
| 39 |
+
| --- | --- | ---: | :---: | :---: | --- |
|
| 40 |
+
| amy | English 🇺🇸 | 1.46 M | **4.13** | [`amy-en-1p46m/`](https://huggingface.co/ampixa/sanoTTS/tree/main/amy-en-1p46m) | <audio controls src="https://huggingface.co/ampixa/sanoTTS/resolve/main/samples/english-amy-1.mp3"></audio> |
|
| 41 |
+
| kristin | English 🇺🇸 | 1.40 M | 4.09 | [`kristin-en-1p4m/`](https://huggingface.co/ampixa/sanoTTS/tree/main/kristin-en-1p4m) | <audio controls src="https://huggingface.co/ampixa/sanoTTS/resolve/main/samples/english-kristin-1.mp3"></audio> |
|
| 42 |
+
| hfc | English 🇺🇸 | 1.83 M | 3.94 | [`hfc-en-1p8m/`](https://huggingface.co/ampixa/sanoTTS/tree/main/hfc-en-1p8m) | <audio controls src="https://huggingface.co/ampixa/sanoTTS/resolve/main/samples/english-hfc-1.mp3"></audio> |
|
| 43 |
+
| amy-small | English 🇺🇸 | 1.08 M | 3.70 | [`amy-en-1p1m/`](https://huggingface.co/ampixa/sanoTTS/tree/main/amy-en-1p1m) | <audio controls src="https://huggingface.co/ampixa/sanoTTS/resolve/main/samples/english-amy-small-1.mp3"></audio> |
|
| 44 |
+
| robot (on-device, int8) | English 🇺🇸 | 745 k | — | not packaged here (int8 MCU format, not fp16) | <audio controls src="https://huggingface.co/ampixa/sanoTTS/resolve/main/samples/mcu-745k.mp3"></audio> |
|
| 45 |
+
| Indonesian | Bahasa | 1.46 M | — | [`id-newstts-1p46m/`](https://huggingface.co/ampixa/sanoTTS/tree/main/id-newstts-1p46m) | <audio controls src="https://huggingface.co/ampixa/sanoTTS/resolve/main/samples/indonesian-1.mp3"></audio> |
|
| 46 |
+
| Vietnamese | Tiếng Việt | 1.46 M | — | [`vi-vais1000-1p46m/`](https://huggingface.co/ampixa/sanoTTS/tree/main/vi-vais1000-1p46m) | <audio controls src="https://huggingface.co/ampixa/sanoTTS/resolve/main/samples/vietnamese-1.mp3"></audio> |
|
| 47 |
+
| Nepali | नेपाली | 1.47 M | — | not exported yet — see `web/voices/nepali/` in the GitHub repo | <audio controls src="https://huggingface.co/ampixa/sanoTTS/resolve/main/samples/nepali-1.mp3"></audio> |
|
| 48 |
+
| Hindi | हिन्दी | 1.50 M | — | not exported yet — see `web/voices/hindi/` in the GitHub repo | <audio controls src="https://huggingface.co/ampixa/sanoTTS/resolve/main/samples/hindi-1.mp3"></audio> |
|
| 49 |
+
| Chinese | 中文 | 1.50 M | — | not exported yet — see `web/voices/chinese/` in the GitHub repo | <audio controls src="https://huggingface.co/ampixa/sanoTTS/resolve/main/samples/chinese-1.mp3"></audio> |
|
| 50 |
|
| 51 |
+
The "robot" row is the same 745k-parameter model that runs on the ESP32-S3 —
|
| 52 |
+
bit-exact with the chip's own output. SCOREQ is only reported for the
|
| 53 |
+
English voices, which share a common eval set; the other languages haven't
|
| 54 |
+
been scored against a comparable reference yet.
|
| 55 |
+
|
| 56 |
+
## Install & use
|
| 57 |
+
|
| 58 |
+
| Platform | Install | Then |
|
| 59 |
+
| --- | --- | --- |
|
| 60 |
+
| Python | `pip install sanotts` | `sanotts say "Hello" --voice amy -o hello.wav` |
|
| 61 |
+
| Web (npm) | `npm install sanotts-web` | `const tts = await SanoTTS.load(); await tts.synthesize('Hello', {voice:'amy'})` |
|
| 62 |
+
| Web (no build) | copy `dist/` + `voices/` | see [Deploy on your own site](https://github.com/Ampixa/sanoTTS#deploy-on-your-own-site) in the GitHub README |
|
| 63 |
+
| Arduino / PlatformIO | zip-install or `lib_deps = https://github.com/Ampixa/sanoTTS.git` | [`arduino/README.md`](https://github.com/Ampixa/sanoTTS/blob/master/arduino/README.md) |
|
| 64 |
+
| Hugging Face | this repo | voice packages above, `manifest.json` + `weights.fp16.bin` per voice |
|
| 65 |
+
| Browser | nothing | [ampixa.github.io/sanoTTS](https://ampixa.github.io/sanoTTS/) |
|
| 66 |
|
| 67 |
+
Pip voices: `amy`, `amy-1p1m`, `amy-1p8m`, `kristin`, `hfc`, `vi`, `id` —
|
| 68 |
+
fetched from the [voices-v1 release](https://github.com/Ampixa/sanoTTS/releases/tag/voices-v1)
|
| 69 |
+
into `~/.cache/sanotts/`. Pure numpy inference, no torch, no onnxruntime.
|
|
|
|
| 70 |
|
| 71 |
## How it stacks up
|
| 72 |
|
| 73 |
+
Open small-scale TTS on an honest gate — a diverse 24-sentence set scored
|
| 74 |
+
with the **same** no-reference suite (SCOREQ / UTMOS are naturalness
|
| 75 |
+
predictors, DNSMOS-SIG is signal quality; higher is better). Parameter
|
| 76 |
+
counts are inference-time and exclude the shared external G2P.
|
| 77 |
|
| 78 |

|
| 79 |
|
| 80 |
+
Kokoro is 45x larger than our largest voice, and 110x larger than our
|
| 81 |
+
smallest. Shipped-file sizes: sanoTTS amy 2.8 MB fp16 and TinyTTS 3.5 MB
|
| 82 |
+
fp16, both verified from the released files; Kokoro's ~330 MB fp32 is its
|
| 83 |
+
widely cited public figure.
|
| 84 |
|
| 85 |
| System | Params | SCOREQ | UTMOS | DNS-SIG |
|
| 86 |
| --- | ---: | :---: | :---: | :---: |
|
|
|
|
| 88 |
| TinyTTS | 1.62 M | 3.94 | 3.65 | **3.62** |
|
| 89 |
| Inflect Nano | 4.63 M | 3.81 | 3.65 | 3.58 |
|
| 90 |
| Kitten TTS nano | 15 M | 3.02 | 3.58 | 3.43 |
|
| 91 |
+
| _reference (~15 M)_ | _~15 M_ | _4.71_ | _4.47_ | _3.65_ |
|
| 92 |
| _Kokoro_ | _82 M_ | _4.89_ | _4.52_ | _3.69_ |
|
| 93 |
|
| 94 |
+
sanoTTS is the **smallest** model here and the **best on naturalness
|
| 95 |
+
(SCOREQ and UTMOS) among everything up to 15M params** — beating TinyTTS
|
| 96 |
+
while being smaller. On DNSMOS-SIG, TinyTTS edges us by 0.01 — no single
|
| 97 |
+
metric tells the whole story. It's the only one that runs a full neural
|
| 98 |
+
stack on a $3 MCU. The frontier only pulls ahead at ~15M-class models and
|
| 99 |
+
Kokoro (82M, 60x larger) — a gap we don't claim to close. Reproduce it with
|
| 100 |
+
`tools/eval_mos_all.py` + `tools/eval_scorecard.py` in the GitHub repo.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
|
| 102 |
## How it works
|
| 103 |
|
| 104 |

|
| 105 |
|
| 106 |
+
espeak-ng provides phoneme IDs; a duration model predicts timing; an
|
| 107 |
+
acoustic model predicts generator latents; a decoder renders 22 kHz audio.
|
| 108 |
The web voices (amy, kristin, hfc, and the other languages) use a compact
|
| 109 |
+
time-domain decoder running in fp32; the 745k on-device model instead uses
|
| 110 |
a quantized int8 iSTFT decoder, sized to fit and run in real time on the
|
| 111 |
+
ESP32-S3. All models are distilled from a Piper/VITS teacher — see
|
| 112 |
+
[`docs/distillation-recipe.md`](https://github.com/Ampixa/sanoTTS/blob/master/docs/distillation-recipe.md)
|
| 113 |
+
in the GitHub repo for the full recipe.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
|
| 115 |
## Deploy
|
| 116 |
|
| 117 |
+
- **ESP32-S3 talking device** — a standalone WiFi dashboard: type text, the
|
| 118 |
+
board phonemizes (on-chip espeak-ng) and speaks. See
|
| 119 |
+
[`mcu/ports/esp32s3/`](https://github.com/Ampixa/sanoTTS/tree/master/mcu/ports/esp32s3).
|
| 120 |
+
- **Browser** — the full stack in WASM, no server. **[▶ Hear and synthesize
|
| 121 |
+
all 9 voices live](https://ampixa.github.io/sanoTTS/)**; source in
|
| 122 |
+
[`web/`](https://github.com/Ampixa/sanoTTS/tree/master/web).
|
| 123 |
- **Other MCUs** — which chips can run it and how well:
|
| 124 |
+
[`docs/mcu-classes-and-porting.md`](https://github.com/Ampixa/sanoTTS/blob/master/docs/mcu-classes-and-porting.md).
|
|
|
|
|
|
|
| 125 |
|
| 126 |
+
## Links
|
|
|
|
|
|
|
|
|
|
| 127 |
|
| 128 |
+
- Source, recipes, eval tooling: [github.com/Ampixa/sanoTTS](https://github.com/Ampixa/sanoTTS)
|
| 129 |
+
- Live browser demo: [ampixa.github.io/sanoTTS](https://ampixa.github.io/sanoTTS/)
|
| 130 |
+
- npm package: [sanotts-web](https://www.npmjs.com/package/sanotts-web)
|
| 131 |
+
- PyPI package: [sanotts](https://pypi.org/project/sanotts/)
|
|
|
|
| 132 |
|
| 133 |
## License
|
| 134 |
|
| 135 |
+
GPLv3 — see [`LICENSE`](https://github.com/Ampixa/sanoTTS/blob/master/LICENSE).
|
| 136 |
+
The pipeline builds on GPLv3 components (notably
|
| 137 |
+
[espeak-ng](https://github.com/espeak-ng/espeak-ng) for G2P, and
|
| 138 |
+
[piper](https://github.com/OHF-Voice/piper1-gpl)), so the project as a
|
| 139 |
whole is GPLv3.
|
| 140 |
|
| 141 |
Copyright (C) 2026 Ampixa.
|
| 142 |
|
|
|
|
| 143 |
## Files here
|
| 144 |
|
| 145 |
+
Each voice directory is a self-contained fp16 package (`weights.fp16.bin` +
|
| 146 |
+
`manifest.json` + `piper-phoneme-config.json` + sibilant-injection
|
| 147 |
+
calibration where applicable), consumable by the `sanotts` Python package
|
| 148 |
+
and the portable C runtime. `samples/` holds the audio clips embedded
|
| 149 |
+
above (mp3, one or two per voice + the on-device `mcu-745k.mp3`).
|