Text to speech in Go, offline
Go has no speech synthesis in its standard library, and the usual answers are a cloud SDK that bills per character or a wrapper around the operating system’s robotic voices. For a service, a bot or a command-line tool, both have drawbacks: your text leaves the machine, or the voice changes from one system to the next.
This guide shows how to call the free SuperClonica engine from Go through a local HTTP server: natural voices, WAV files you keep, and plain net/http on any platform.
Ways to speak from Go
A service that speaks often is cheaper and more private with its own speech server.
- Cloud SDKs: natural voices, but an account, a key, a bill per character and your text on their servers.
- Operating-system voices through a wrapper: offline, but robotic and different on every platform.
- SuperClonica: a local HTTP server with natural, character and custom voices, offline, wherever Docker runs.
- It speaks 31 languages; Chinese is not supported yet.
Hear the voices free online
Before you write code, open the page of any voice, type a line of 30 to 100 characters, choose the language and press Voice it. In a few seconds you hear the voice your app could have, with no account.
Start the speech server
An NVIDIA graphics card makes it faster with the cuda image, but it is not required.
- Start the server on any CPU: docker run -d -p 8000:8000 -v superclonica-models:/models superclonica/superclonica:cpu.
- The first start downloads the Supertonic 3 model, about 385 MB, into the volume; GET /health tells you when it is ready.
- POST /synthesize takes JSON with text, voice, lang, seed and normalize, and answers with a 16-bit mono WAV.
- voice is a stock name from M1 to M5 and F1 to F5, or the JSON content of a voice file.
Call it from Go
With bytes, io, net/http and os from the standard library, a request takes a few lines: voice, _ := os.ReadFile("voice.json"); body := append([]byte(`{"text": "Hello from Go!", "lang": "en", "seed": 7, "voice": `), append(voice, '}')...); res, _ := http.Post("http://localhost:8000/synthesize", "application/json", bytes.NewReader(body)); defer res.Body.Close(); out, _ := os.Create("hello.wav"); defer out.Close(); io.Copy(out, res.Body) Handle the errors in real code instead of discarding them. For a stock voice, send "voice": "F1" instead of the file. The same text, voice and seed always give the same audio, so cache files for phrases that repeat.
Production habits
Reuse one http.Client with a sensible timeout. Do not expose port 8000 to the internet as it is: set SUPERCLONICA_API_KEY and send it as a Bearer token, or keep the server on a private network. Run more copies of the model with SUPERCLONICA_LANES if many requests arrive at once, and measure on your hardware: in our tests a short phrase took 0.12 s on an RTX 5070 Ti and 0.43 s on four CPU threads.