Text to speech in JavaScript and Node.js
JavaScript has text to speech built in: the Web Speech API in the browser. It is free and needs no setup, but the voices depend on the visitor’s system, and you cannot save the result as a file. When you need the same natural voice everywhere, character voices or audio files, you need a speech engine.
This guide compares both ways honestly and shows how to call the free SuperClonica engine from Node.js and from a web app through a local HTTP server.
Two ways to speak from JavaScript
For a quick read-aloud button the Web Speech API is fine; for a product voice, use an engine.
- Web Speech API: one line in the browser, speechSynthesis.speak(new SpeechSynthesisUtterance("Hello!")), with the voices of the operating system or browser.
- Its voices differ between Windows, macOS, Android and browsers, some of them are online services, and the audio cannot be saved as a file.
- SuperClonica: a local server with the same natural voice on every device, character and custom voices, and WAV files you can store and cache.
- It speaks 31 languages; Chinese is not supported yet.
Hear the engine 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 a local 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 parsed content of a voice file.
Call it from Node.js and the browser
In Node.js 18 or newer, fetch is built in, so with fs imported a request takes a few lines: const voice = JSON.parse(fs.readFileSync("voice.json", "utf8")); const res = await fetch("http://localhost:8000/synthesize", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ text: "Hello from Node!", voice, lang: "en", seed: 7 }) }); fs.writeFileSync("hello.wav", Buffer.from(await res.arrayBuffer())); In a web app, keep the speech server private and call it from your own backend; the page then receives the audio as a blob and plays it with new Audio(URL.createObjectURL(blob)).play(). The same text, voice and seed always give the same file, so cache audio for phrases that repeat.
Production habits
Do not expose port 8000 to the internet as it is: set SUPERCLONICA_API_KEY and send it as a Bearer token, put the server behind a reverse proxy with HTTPS, or keep it 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.