k8bit/supertonic is a Docker image providing an OpenAI-compatible REST API for
Supertonic 3 TTS — a 99M-parameter, CPU-native
TTS system powered by ONNX Runtime. It runs entirely on-device with no GPU required.
The API is a drop-in replacement for OpenAI's /v1/audio/speech endpoint.
Key specs:
Supertone/supertonic-3 (~200 MB, downloaded automatically on first start)No Unraid, no GPU, no configuration required. You just need Docker.
docker run (instant test)docker run --rm -p 8030:8000 \
-v supertonic-models:/models \
k8bit/supertonic:latest
The first run downloads the ONNX model (~200 MB). Watch the logs — when you see
API endpoint : http://0.0.0.0:8000/v1/audio/speech it's ready.
Then test it:
# Health check
curl http://localhost:8030/v1/health
# List voices
curl http://localhost:8030/v1/styles | python3 -m json.tool
# Synthesise speech → test.wav
curl -X POST http://localhost:8030/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"supertonic-3","input":"Hello! Supertonic is working.","voice":"F1","response_format":"wav"}' \
--output test.wav
The named volume supertonic-models is reused on subsequent runs so the model
is only downloaded once.
Save the following as docker-compose.yml (or use the provided supertonic-dockhand.yml
directly with docker compose -f supertonic-dockhand.yml up -d):
name: supertonic
services:
supertonic:
image: k8bit/supertonic:latest
container_name: supertonic
restart: unless-stopped
ports:
- "${API_PORT:-8030}:8000"
volumes:
- ${MODELS_DIR:-./models}:/models
- ${VOICES_DIR:-./voices}:/reference_voices:ro
environment:
SUPERTONIC_MODEL: ${SUPERTONIC_MODEL:-supertonic-3}
CORS_ORIGINS: ${CORS_ORIGINS:-*}
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:8000/v1/health"]
interval: 30s
timeout: 10s
retries: 10
start_period: 600s
Start:
mkdir -p models voices
docker compose up -d
docker compose logs -f
Stop:
docker compose down
The models/ directory is created locally and persists across restarts.
Drop Voice Builder JSON files into voices/ to add custom voices (see Voice cloning below).
.env or inline)| Variable | Default | Description |
|---|---|---|
API_PORT | 8030 | Host port to expose the API on |
MODELS_DIR | ./models | Path to persist the ONNX model cache |
VOICES_DIR | ./voices | Path to a folder of Voice Builder JSON files |
SUPERTONIC_MODEL | supertonic-3 | Model version (supertonic-3 / supertonic-2 / supertonic) |
CORS_ORIGINS | * | CORS origins; * is required for the browser-based HTML tools |
Create a .env file next to docker-compose.yml to override any of these:
API_PORT=8030
MODELS_DIR=/opt/supertonic/models
VOICES_DIR=/opt/supertonic/voices
SUPERTONIC_MODEL=supertonic-3
Once running, the full Swagger UI is available at:
http://localhost:8030/docs
Search for supertonic in the CA store. If not yet listed, use Option B.
https://raw.githubusercontent.com/k8bit/docker-templates/main/supertonic.xml
Or download supertonic-unraid.xml and import it manually: go to Docker → Templates →
Add New Template, paste the XML contents into the editor, then save.
| Container path | Suggested host path | Purpose |
|---|---|---|
/models | /mnt/user/ai/supertonic/models | ONNX model cache (~200 MB) and custom voice styles |
/reference_voices | /mnt/user/ai/voices | Voice Builder JSON files for custom voices |
The /reference_voices path can be shared with Fish Speech and Higgs Audio — all three services
look for files in the same directory (other services' .s2voice files and .wav/.txt pairs
are simply ignored by Supertonic).
| Setting | Default | Notes |
|---|---|---|
| API Port | 8030 | Host port for the OpenAI-compat API — change if in use |
| Subwave Bridge Port | 5001 | Host port for the Subwave Remote bridge — change if in use |
| Model Version | supertonic-3 | 31-language latest; options: supertonic-2 (5 langs), supertonic (English only) |
| CORS Origins | * | Leave as * for browser-based HTML tools |
| Bridge Language | en | BCP-47 language code sent to Supertonic for Subwave speech (e.g. fr, ja, na) |
| Bridge Speed | 1.0 | Speed multiplier for Subwave TTS output (0.7–2.0) |
On first launch the container downloads the ONNX model (~200 MB). This takes 1–3 minutes depending on connection speed.
Watch progress:
docker logs -f supertonic
Ready when you see:
API endpoint : http://0.0.0.0:8000/v1/audio/speech (OpenAI-compat)
Subwave bridge : http://0.0.0.0:5001/speak (Remote engine)
Interactive docs: http://0.0.0.0:8000/docs
/mnt/user/ai/
├── supertonic/
│ └── models/ # ONNX model cache + custom voice styles (~200 MB total)
└── voices/ # Voice Builder JSON files: VOICENAME.json
name: supertonic
services:
supertonic:
image: k8bit/supertonic:latest
container_name: supertonic
restart: unless-stopped
ports:
- "${API_PORT:-8030}:8000"
- "${BRIDGE_PORT:-5001}:5001"
volumes:
- ${MODELS_DIR:-/mnt/user/ai/supertonic/models}:/models
- ${VOICES_DIR:-/mnt/user/ai/voices}:/reference_voices:ro
environment:
SUPERTONIC_MODEL: ${SUPERTONIC_MODEL:-supertonic-3}
CORS_ORIGINS: ${CORS_ORIGINS:-*}
BRIDGE_LANG: ${BRIDGE_LANG:-en}
BRIDGE_SPEED: ${BRIDGE_SPEED:-1.0}
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:8000/v1/health"]
interval: 30s
timeout: 10s
retries: 10
start_period: 600s
# Volumes
MODELS_DIR=/mnt/user/ai/supertonic/models
VOICES_DIR=/mnt/user/ai/voices
# Ports
API_PORT=8030 # OpenAI-compat API
BRIDGE_PORT=5001 # Subwave Remote bridge
# Model version: supertonic-3 | supertonic-2 | supertonic
SUPERTONIC_MODEL=supertonic-3
# CORS origins (* = allow all; required for browser tools)
CORS_ORIGINS=*
# Subwave bridge settings
BRIDGE_LANG=en # BCP-47 language code (en, fr, ja, …) or na for auto
BRIDGE_SPEED=1.0 # Speed multiplier (0.7 = slow, 1.0 = normal, 2.0 = fast)
docker compose up -d
docker logs -f supertonic
SUB/WAVE is a self-hosted AI radio station (Icecast + Liquidsoap + AI DJ). Its Remote TTS engine speaks a simple HTTP contract:
GET /health → { "ok": true }
POST /speak → { "text": "...", "voice": "..." } → WAV bytes
This image includes a built-in bridge (bridge.py) that listens on port 5001 and translates
that contract into Supertonic's native /v1/tts API. No extra container or sidecar needed —
just expose the port and point Subwave at it.
Subwave's Cloud/OpenAI engine sends response_format: "mp3" which Supertonic cannot produce
(the ONNX model outputs 44.1 kHz audio; libsndfile's MP3 encoder is pinned to standard sample
rates). Using the Remote engine and the built-in bridge sidesteps this entirely.
1. Expose the bridge port
The bridge runs on port 5001 inside the container. Map it to your host:
Unraid: the template includes a "Subwave Bridge Port" field (default 5001) — it is
already there, just confirm the port isn't in use.
docker-compose: ${BRIDGE_PORT:-5001}:5001 is already in the generated compose file.
docker run:
docker run --rm \
-p 8030:8000 \
-p 5001:5001 \
-v supertonic-models:/models \
k8bit/supertonic:latest
2. Verify the bridge is up
curl http://192.168.1.100:5001/health
# → {"ok":true}
3. Configure Subwave
In the Subwave admin UI:
http://192.168.1.100:5001 (use the IP your Subwave controller can reach;
not 127.0.0.1 unless they're on the same host)4. Set voices on personas
Each Subwave persona has a voice field. The bridge forwards this string verbatim to
Supertonic as the voice parameter. Use any built-in or custom imported voice:
| Voice | Character |
|---|---|
M1 | Lively, upbeat male |
M2 | Deep, serious male |
M3 | Polished, authoritative male |
M4 | Soft, friendly male |
M5 | Warm, soft-spoken male |
F1 | Calm, composed female |
F2 | Bright, cheerful female |
F3 | Clear, announcer-style female |
F4 | Crisp, confident female |
F5 | Kind, gentle female |
Custom voices imported from Voice Builder are also valid — use the filename stem (e.g. a file
named milo.json in /reference_voices/ is available as voice milo).
| Variable | Default | Description |
|---|---|---|
BRIDGE_PORT | 5001 | Host and container port the bridge listens on |
BRIDGE_LANG | en | BCP-47 language code sent to Supertonic (e.g. en, fr, ja, na) |
BRIDGE_SPEED | 1.0 | Playback speed multiplier (0.7 = slow, 1.0 = normal, 2.0 = fast) |
BRIDGE_LANG is a station-wide setting — all DJ speech uses the same language code. Use na
(language-agnostic mode) if your station plays multilingual content and you want Supertonic to
infer the language from the text automatically.
Supertonic's open-weight release uses fixed built-in voices. Custom voice cloning is provided through Supertone Voice Builder, which converts a short recording into a downloadable JSON voice profile.
Place the downloaded JSON file in your /reference_voices/ host directory:
/mnt/user/ai/voices/
└── myvoice.json ← Voice Builder export (Supertonic 3 version)
The filename stem (myvoice) becomes the voice name used in API calls. Restart the container
to import new voices:
docker restart supertonic
Or import at runtime without restarting:
curl -X POST http://trogdor:8030/v1/styles/import \
-F "file=@/path/to/myvoice.json"
curl http://trogdor:8030/v1/styles | jq '.styles[].name'
Returns all voices — both built-in (M1–M5, F1–F5) and any imported custom voices.
| Voice | Character | Best for |
|---|---|---|
| M1 | Lively, upbeat male | Promotional videos, casual narration |
| M2 | Deep, serious male | Corporate content, documentaries |
| M3 | Polished, authoritative male | Business presentations, high-trust narration |
| M4 | Soft, friendly male | Educational content, onboarding guides |
| M5 | Warm, soft-spoken male | Audiobooks, relaxation, bedtime stories |
| F1 | Calm, composed female | Customer service, guided instructions |
| F2 | Bright, cheerful female | Youth content, playful ads |
| F3 | Clear, announcer-style female | Commercials, news narration |
| F4 | Crisp, confident female | Business explainers, pitch decks |
| F5 | Kind, gentle female | Audiobooks, wellness content |
curl http://trogdor:8030/v1/health
{
"status": "ok",
"model": "supertonic-3",
"sample_rate": 44100,
"version": "1.3.1",
"voices_loaded": 12
}
curl http://trogdor:8030/v1/styles | jq '.styles[] | {name: .name, gender: .gender}'
curl -X POST http://trogdor:8030/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"model": "supertonic-3",
"input": "Hello, this is a test of the Supertonic TTS system.",
"voice": "F1",
"response_format": "wav"
}' \
--output test.wav
Request fields:
| Field | Type | Default | Description |
|---|---|---|---|
model | string | — | Any value accepted (supertonic-3, tts-1, etc.) |
input | string | — | Text to synthesise. Supports expression tags (see below) |
voice | string | M1 | Built-in voice name or custom voice stem from Voice Builder JSON |
response_format | string | wav | wav, flac, or ogg |
speed | float | 1.0 | Playback speed (passed through; see /v1/tts for full control) |
curl -X POST http://trogdor:8030/v1/tts \
-H "Content-Type: application/json" \
-d '{
"text": "Supertonic 3 runs fast even on a Raspberry Pi.",
"voice": "M1",
"lang": "en",
"total_steps": 8,
"speed": 1.05
}' \
--output output.wav
Native-only fields:
| Field | Type | Default | Description |
|---|---|---|---|
lang | string | en | Language code (e.g. en, fr, ja). Use na for language-agnostic mode |
total_steps | int | 8 | Quality steps: 5 (fast/low) to 12 (slow/high). Default 8 is a good balance |
speed | float | 1.0 | Speed multiplier (0.7–2.0) |
Inline tags add natural expression to speech. Place them in the input/text field:
{
"input": "Wait, really? <laugh> I can't believe it! <breath> Okay, let me think about this."
}
Available tags: <laugh>, <breath>, <sigh>, and 7 additional tags. See the
Supertonic 3 model card for the full list.
curl -X POST http://trogdor:8030/v1/tts/batch \
-H "Content-Type: application/json" \
-d '{
"items": [
{"text": "First sentence.", "voice": "M1", "lang": "en"},
{"text": "Second sentence.", "voice": "F1", "lang": "en"}
]
}'
Returns up to 64 items as base64-encoded audio per item. See /docs for the full schema.
curl -X POST http://trogdor:8030/v1/styles/import \
-F "file=@/path/to/myvoice.json"
The voice becomes available immediately without a restart.
| Variable | Default | Description |
|---|---|---|
SUPERTONIC_MODEL | supertonic-3 | Model version: supertonic-3 | supertonic-2 | supertonic |
CORS_ORIGINS | * | CORS origins for browser clients. * = allow all |
PORT | 8000 | Internal container port (do not change unless you know why) |
HF_HOME | /models/hf | HuggingFace hub cache directory |
XDG_CACHE_HOME | /models/cache | Linux XDG cache directory — supertonic caches ONNX model files and custom voice styles here |
Supertonic 3 natively supports WAV, FLAC, and OGG output. MP3 is not available because the underlying ONNX model outputs 44.1 kHz audio and the libsndfile Opus/MP3 encoders are pinned to standard sample rates (8/16/24/48 kHz). To get MP3, transcode after receipt:
curl -X POST http://trogdor:8030/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{"model":"supertonic-3","input":"Hello world","voice":"M1","response_format":"wav"}' \
| ffmpeg -i pipe:0 -codec:a libmp3lame -q:a 2 output.mp3
Symptom: Long pause before "Server ready" message; or timeout.
Cause: On first run, the container downloads the Supertonic 3 ONNX model files (~200 MB) from Hugging Face. On a fast connection this takes 1–3 minutes; on a slow connection it can take longer.
Fix: Wait. The startup timeout in the entrypoint is 10 minutes. Watch the logs:
docker logs -f supertonic
Once downloaded, the model is cached in /models/cache/ and subsequent starts are fast.
Symptom: ✗ Failed: myvoice in the startup log.
Cause A: The JSON file is a Supertonic 2 export, not a Supertonic 3 export. They are not interchangeable — download the correct version from My Page on Voice Builder.
Cause B: The voice name conflicts with a built-in voice (M1–M5, F1–F5). Rename the JSON file to avoid these names.
Fix: Check that the JSON was downloaded as the Supertonic 3 variant, rename if needed, and restart the container.
Symptom: API calls from a locally-opened HTML file return "Failed to fetch".
Fix: Ensure CORS_ORIGINS=* is set in the container environment (the default). This is
passed to supertonic serve --cors "*" which enables the CORS middleware in the built-in
FastAPI server.
Cause: total_steps is too low. The default of 8 is a good balance; increase to 10–12
for higher quality at the cost of speed.
Use the native /v1/tts endpoint to set total_steps directly:
curl -X POST http://trogdor:8030/v1/tts \
-d '{"text":"Hello","voice":"M1","lang":"en","total_steps":12}' \
--output output.wav
Use the speed parameter (0.7 = slow, 1.0 = normal, 2.0 = fast):
{"model":"supertonic-3","input":"Hello","voice":"M1","speed":0.85}
All three containers can share the same host /mnt/user/ai/voices/ directory:
# Fish Speech
- /mnt/user/ai/voices:/reference_voices:ro
# Higgs Audio
- /mnt/user/ai/voices:/reference_voices:ro
# Supertonic
- /mnt/user/ai/voices:/reference_voices:ro
Each service only reads the file types it understands:
| File type | Used by |
|---|---|
NAME.wav + NAME.txt | Fish Speech (pre-encoded), Higgs Audio (zero-shot cloning) |
NAME.json (Voice Builder export) | Supertonic |
NAME.s2voice | Fish Speech internal profile (ignored by others) |
There is no conflict — the extra files are silently ignored.
Once running, the full interactive API documentation is available at:
http://[UNRAID_IP]:8030/docs
This Swagger UI lets you test all endpoints directly from the browser, including voice listing, synthesis, and voice import.
Content type
Image
Digest
sha256:5471d9cf2…
Size
114.4 MB
Last updated
3 months ago
docker pull k8bit/supertonic