The bot can synthesize voice replies through four providers. They're
all opt-in (audio replies are off until you /tts on), and you can
switch between them at any time without restarting Docker.
| Provider | API key needed? | Free tier | Notes |
|---|---|---|---|
| edge | ❌ No | Unlimited (Microsoft public endpoint) | Recommended fallback. ~400 neural voices, 140+ locales. |
| speechify | ✅ Yes | 50 000 chars/month | Most natural-sounding voices in English. |
| openai | ✅ Yes | No free tier | Use any OpenAI-compatible /audio/speech endpoint. |
| ✅ Yes (service account JSON) | $$$ small free tier | Studio voices are excellent but pricey. |
TL;DR: if you don't want to configure anything, run /tts provider edge
and /tts on. You'll get a free, high-quality voice immediately.
The /tts command is the source of truth for runtime overrides. Anything
you set with /tts ... lives in settings.json and survives restarts.
/tts # show current config + how to change it
/tts on | off # toggle audio replies globally
/tts provider edge|speechify|openai|google
/tts voice <id> # change voice (use /tts list to see options)
/tts speed 0.5..2.0 # speak faster or slower
/tts list [provider] [locale] # list available voices, optionally filtered
Examples:
/tts provider edge
/tts list edge es # all Spanish-locale Edge voices
/tts voice es-ES-ElviraNeural
/tts speed 1.15
/tts on
The status line shows the source of each setting (env from .env,
override from /tts, default when neither is set):
🎙 TTS settings
Enabled: ✓
Provider: edge (override)
Voice: es-ES-ElviraNeural (override)
Speed: 1.15
The MCP layer exposes three tools so the assistant can change TTS in the middle of a conversation without you running commands:
tts_get_settings— read current state, including the source of each fieldtts_set_settings({ provider?, voice?, speed?, enabled? })— update any subsettts_list_voices({ provider?, locale?, limit? })— discover voice IDs
So you can simply ask:
"switch to a Spanish female voice"
and the assistant calls tts_list_voices({ provider: "edge", locale: "es" }),
picks one (e.g. es-ES-ElviraNeural), and calls
tts_set_settings({ voice: "es-ES-ElviraNeural" }). Persists across restarts.
Edge uses Microsoft's public WebSocket endpoint — the same one that powers Edge browser's "Read Aloud". No account, no API key, no quota that I'm aware of for personal use.
- Voice IDs follow the format
{locale}-{Name}Neural, e.g.en-US-AriaNeural,es-ES-ElviraNeural,de-DE-KatjaNeural. - The full catalog is fetched live from Microsoft on first use and
cached in-process. Use
/tts list edge <locale>ortts_list_voices({ provider: "edge", locale })to discover. - Speed is implemented via SSML
<prosody rate="...">and clamped to ±50% (so 0.5x and 2.0x map to -50%/+50%).
If Microsoft ever changes the endpoint, the msedge-tts package
maintainers usually update within a few days. As a fallback, drop down
to a different provider via /tts provider <name>.
Speechify's free tier (50 000 chars/month) is generous for personal use. Get a key at api.speechify.ai.
SPEECHIFY_API_KEY=...
Voices: henry, matthew, kristy, stacy, etc. The catalog is
hard-coded in src/tts/voices.ts since Speechify doesn't expose a
free /voices API.
Any provider speaking the OpenAI /audio/speech shape works (OpenAI
proper, OpenRouter, local lmstudio, etc).
TTS_API_URL=https://api.openai.com/v1
TTS_API_KEY=sk-...
TTS_MODEL=gpt-4o-mini-tts # or any model your endpoint supports
Voices: alloy, echo, fable, onyx, nova, shimmer. Free-form
voice IDs are accepted (no client-side validation), since some
providers expose extra voices not in the OpenAI list.
Highest quality of the bunch (Studio voices are scary good) but the setup is the most involved. You need a service-account JSON with Cloud TTS API enabled, mounted into the bot container.
GOOGLE_APPLICATION_CREDENTIALS=/run/secrets/gcp-tts.json
Voices: en-US-Studio-O, en-US-Studio-Q, en-US-Neural2-F,
es-ES-Neural2-A, etc.
When the bot needs to synthesize a reply, it goes through this
resolution order (see src/tts/config-resolver.ts):
uiPreferences.tts.provider(set via/tts providerortts_set_settings)- else
TTS_PROVIDERfrom.env - else
"openai"(the legacy default)
Voice goes through the same priority:
uiPreferences.tts.voice(override)TTS_VOICEfrom.envif it matches the chosen provider- else
defaultVoiceFor(provider, BOT_LOCALE)— locale-aware default
Speed defaults to 1.0× unless overridden.
/tts onsays "not configured" — your provider has no creds available. Either configure them in.envor run/tts provider edgeto switch to the no-key fallback first.- Voice doesn't change despite
/tts voice X— the static catalogs (Speechify/Google) reject unknown voice IDs to avoid silent failures. Use/tts listto see what's valid. - Edge fails with WebSocket error — likely a transient Microsoft
side issue. Retry; if persistent,
/tts provider speechify(or another configured one) until Microsoft is back. - Chinese, Russian, Arabic voices — Edge has them. Try
/tts list edge zh,/tts list edge ru,/tts list edge ar.