GUIDE

Text to speech in C# and .NET, offline

The classic way to speak from C# is System.Speech: a few lines, the voices of Windows, no internet. It still works, but only on Windows, the voices sound dated, and they differ from one machine to the next. Cloud services sound better but send every text away and bill by the character.

This guide shows a third way: the free SuperClonica engine as a local HTTP server that any .NET app can call on Windows, Linux or macOS, with natural voices and WAV files you can keep.

Three ways to speak from .NET

For a quick prototype on Windows, System.Speech is fine; for a product voice that sounds the same everywhere, use an engine.

  • System.Speech: built into Windows, offline, robotic voices, Windows only.
  • Cloud speech APIs: natural voices, but an account, a key, a bill per character and your text on their servers.
  • SuperClonica: a local server with natural voices, character and custom voices, offline, on any operating system that runs Docker.
  • 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 C#

With System.Net.Http, System.Text and System.Text.Json.Nodes, a request takes a few lines in .NET 6 or newer: using var http = new HttpClient(); var voice = JsonNode.Parse(File.ReadAllText("voice.json")); var body = new JsonObject { ["text"] = "Hello from C#!", ["voice"] = voice, ["lang"] = "en", ["seed"] = 7 }; var res = await http.PostAsync("http://localhost:8000/synthesize", new StringContent(body.ToJsonString(), Encoding.UTF8, "application/json")); File.WriteAllBytes("hello.wav", await res.Content.ReadAsByteArrayAsync()); For a stock voice, set ["voice"] = "F1" instead of loading a file. The same text, voice and seed always give the same audio, so cache files for phrases that repeat, and play them with the audio library your app already uses.

Production habits

Keep one HttpClient for the life of the app. 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.