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
sanoTTS β a tiny neural voice that runs anywhere
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.
| Parameter range | 294,279 β 2,272,145 |
| Runs real-time on a $3 microcontroller | ESP32-S3, out a GPIO into an LM386 |
| Runs in the browser | WebAssembly, no server |
| Per-voice footprint | 337 KB to 8.7 MB, zero dependencies (espeak-ng phonemizer included) |
| Coverage | 11 voices across 6 languages β English, Nepali (ΰ€¨ΰ₯ΰ€ͺΰ€Ύΰ€²ΰ₯), Hindi (ΰ€Ήΰ€Ώΰ€¨ΰ₯ΰ€¦ΰ₯), Vietnamese (TiαΊΏng Viα»t), Indonesian (Bahasa), Chinese (δΈζ) |
| License | open source, GPL-3.0 |
Live demo: tts.ampixa.com/sanoTTS β every voice synthesizes your text live in the browser, no server, no upload.
Download
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.
Samples
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 and the GitHub repo's
web/voices/ β their packages haven't been exported yet.
| Voice | Language | Params | SCOREQ | Package here | Sample |
|---|---|---|---|---|---|
| heart | English πΊπΈ | 2.27 M | 3.48 | heart/ |
|
| hfc | English πΊπΈ | 1.83 M | 3.94 | hfc-en-1p8m/ |
|
| amy | English πΊπΈ | 1.46 M | 4.13 | amy-en-1p46m/ |
|
| kristin | English πΊπΈ | 1.40 M | 4.09 | kristin-en-1p4m/ |
|
| amy-small | English πΊπΈ | 1.08 M | 3.70 | amy-en-1p1m/ |
|
| robot (on-device, int8) | English πΊπΈ | 567 k | β | not packaged here (int8 MCU format) | |
| heart-nano | English πΊπΈ | 294 k | 2.29 | heartnano/ |
|
| Indonesian | Bahasa | 1.46 M | β | id-newstts-1p46m/ |
|
| Vietnamese | TiαΊΏng Viα»t | 1.46 M | β | vi-vais1000-1p46m/ |
|
| Nepali | ΰ€¨ΰ₯ΰ€ͺΰ€Ύΰ€²ΰ₯ | 1.47 M | β | not exported yet β see web/voices/nepali/ in the GitHub repo |
|
| Hindi | ΰ€Ήΰ€Ώΰ€¨ΰ₯ΰ€¦ΰ₯ | 1.50 M | β | not exported yet β see web/voices/hindi/ in the GitHub repo |
|
| Chinese | δΈζ | 1.50 M | β | not exported yet β see web/voices/chinese/ in the GitHub repo |
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.
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.
Install & use
| Platform | Install | Then |
|---|---|---|
| Python | pip install sanotts |
sanotts say "Hello" --voice heart -o hello.wav |
| Web (npm) | npm install sanotts-web |
const tts = await SanoTTS.load(); await tts.synthesize('Hello', {voice:'heart'}) |
| Web (no build) | copy dist/ + voices/ |
see Deploy on your own site in the GitHub README |
| Arduino / PlatformIO | zip-install or lib_deps = https://github.com/Ampixa/sanoTTS.git |
arduino/README.md |
| Hugging Face | this repo | the voice packages above, downloaded for you by pip install sanotts |
| Browser | nothing | tts.ampixa.com/sanoTTS |
Pip voices: heart, hfc, amy-1p8m, amy, kristin, vi, id,
amy-1p1m, heart-nano. Pure numpy inference, no torch, no onnxruntime.
How it stacks up
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.
| System | Params | SCOREQ | UTMOS | DNS-SIG |
|---|---|---|---|---|
| sanoTTS (amy) | 1.46 M | 4.13 | 4.10 | 3.61 |
| TinyTTS | 1.62 M | 3.94 | 3.65 | 3.62 |
| Inflect Nano | 4.63 M | 3.81 | 3.65 | 3.58 |
| Kitten TTS nano | 15 M | 3.02 | 3.58 | 3.43 |
| Piper (our teacher) | ~15 M | 4.71 | 4.47 | 3.65 |
| Kokoro | 82 M | 4.89 | 4.52 | 3.69 |
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.
How it works
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
in the GitHub repo for the full recipe.
Deploy
- ESP32-S3 talking device β a standalone WiFi dashboard: type text, the
board phonemizes (on-chip espeak-ng) and speaks. See
mcu/ports/esp32s3/. Board-by-board measurements are inBOARDS.md; the silicon figures there are for theen_us_e12nanolineage, which is a sibling ofheart-nano, not the same weights. - Browser β the full stack in WASM, no server. βΆ Hear and synthesize
all 11 voices live; source in
web/. - Other MCUs β which chips can run it and how well:
docs/mcu-classes-and-porting.md.
Links
- Source, recipes, eval tooling: github.com/Ampixa/sanoTTS
- Live browser demo: tts.ampixa.com/sanoTTS
- npm package: sanotts-web
- PyPI package: sanotts
License
GPLv3 β see LICENSE.
The pipeline builds on GPLv3 components (notably
espeak-ng for G2P, and
piper), so the project as a
whole is GPLv3.
Copyright (C) 2026 Ampixa.
Files here
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.
- Downloads last month
- 2,512
32-bit

