Text to speech that runs on your machine.
Twenty voices across ten languages, and a new one cloned from ten seconds of audio. Nothing leaves the machine you run it on.
Hear it
Twenty voices, ten languages.
English, Spanish, French, German, Italian, Portuguese, Polish, Dutch, Swedish and Danish. Every voice is enrolled from a consented donation or a CC0 / CC-BY corpus, with the source named.
Hear two now.
Both clips read the same sentence from Alice’s Adventures in Wonderland.
One seeded sample per voice, playable in the browser. The sources and the licences are on the same page. Open the demo to hear each enrollment recording next to the speech it produced, and the other eighteen voices.
We have evaluated English by ear. We do not speak the other nine languages well enough to judge them reliably, so feedback from native speakers is very welcome.
Where it runs
Laptops, GPUs and edge devices.
Faster than real time on datacentre GPUs, on desktop GPUs, on Apple Silicon and on an embedded board. Every figure is one loudkit bench run on the named machine. The hardware, the method and the commands.
requires an NVIDIA GPU
NVIDIA CUDA
PyTorch, from an embedded board to a datacentre card.
| Host | Eager | CUDA graphs |
|---|---|---|
| A100 | 2.16× | 8.12× |
| L4 | 2.20× | 7.56× |
| RTX 3090 | 3.39× | 7.47× |
| T4 | 2.10× | 5.46× |
| GTX 1080 Ti | 1.95× | 1.89× * |
| Jetson Orin | 0.73× | 1.83× |
* Pascal cannot capture graphs: on the 1080 Ti the flag runs eager.
Batch throughput, RTX 3090 with CUDA graphs
Many texts at once. Each number is the total audio produced per second of wall clock across the whole batch, not the speed of one synthesis.
What it measures, and the full table. The highest measured aggregate is 170.8× on an A100 at batch 64.
requires Apple Silicon
Apple GPU
--device mpsThe generator runs on the CPU and the renderer on the GPU. A CoreML renderer ships for Swift.
requires no PyTorch
ONNX Runtime
Runs without PyTorch, on ONNX Runtime.
an embedded NVIDIA GPU
Jetson Orin Nano
--device cuda --cuda-graphs, measured on the boardThe same engine, the same conformance fixture, on a board with no discrete GPU.
Every figure is a multiple of real time. 3× real time means one minute of audio in twenty seconds. Under 1× is slower than real time.
Five languages, one engine
Ports, not wrappers.
Python is the reference implementation. Swift, Go, Rust and TypeScript are full ports of it, and all five are held to the same conformance fixture, so the same text and the same voice give the same speech tokens in every one.
Install
Install, then speak.
Add the package, load a checkpoint, pick a voice, get a waveform. Python fetches what it needs on the first call. The ONNX ports read files from disk, so they fetch first into loudr-1/, which is what the paths below are relative to.
pip install "loudkit[torch,audio,hub]"import loudkit as lk
engine = lk.load("loudreader/loudr-1")voice = lk.voice("kathleen", repo="loudreader/loudr-1")engine.synthesize("Hello from loudkit.", voice, seed=7).save("hello.wav")npm i loudkitpip install "loudkit[hub]"loudkit download loudreader/loudr-1 --for onnx --local-dir loudr-1import { Engine, loadVoice } from "loudkit";
const engine = await Engine.load( "loudr-1/loudr-1.safetensors", "loudr-1/onnx", "loudr-1/tokenizer.json");const voice = loadVoice("loudr-1/voices/kathleen.safetensors");
const result = await engine.synthesize("Hello from loudkit.", voice, 7);// result.audio is a Float32Array at result.sampleRate.go get github.com/loudreader/loudkit/gopip install "loudkit[hub]"loudkit download loudreader/loudr-1 --for onnx --local-dir loudr-1// Point onnxruntime_go at libonnxruntime first. See the Go guide.eng, _ := engine.Load( "loudr-1/loudr-1.safetensors", "loudr-1/onnx", "loudr-1/tokenizer.json")defer eng.Close()v, _ := voice.Load("loudr-1/voices/kathleen.safetensors")
// After the seed: language, speed, previousTokens, shouldCancel.audio, _, _, _, sr, _, _ := eng.SynthesizeLong( "Hello from loudkit.", v, 7, "", 1.0, nil, nil)loudkit = "0.1"pip install "loudkit[hub]"loudkit download loudreader/loudr-1 --for onnx --local-dir loudr-1let mut eng = Engine::load( "loudr-1/loudr-1.safetensors", "loudr-1/onnx", "loudr-1/tokenizer.json")?;let v = voice::load("loudr-1/voices/kathleen.safetensors")?;
// After the seed: language, speed, previous_tokens, should_cancel.let (audio, tokens, _, sr, _, _) = eng.synthesize_long("Hello from loudkit.", &v, 7, None, 1.0, None, None)?;// Swift Package Manager.package(url: "https://github.com/loudreader/loudkit", from: "0.1.0")pip install "loudkit[hub]"loudkit download loudreader/loudr-1 --for coreml --local-dir loudr-1import Foundationimport LoudKit
let engine = try Engine.load( checkpoint: URL(fileURLWithPath: "loudr-1/loudr-1.safetensors"))let voice = try VoiceProfile.load( url: URL(fileURLWithPath: "loudr-1/voices/kathleen.safetensors"))
let result = try engine.synthesize("Hello from loudkit.", voice: voice, seed: 7)try result.save(to: URL(fileURLWithPath: "hello.wav"))The same seed and voice give the same speech tokens in all five. The four ports load graphs exported once from a Python checkout: ONNX for TypeScript, Go and Rust, CoreML for Swift. Each guide has that step.
Where to go next
What it promises.
Responsible use.
The tool clones a voice from ten seconds of audio and includes C2PA Content Credentials in saved WAVs by default. Consent is yours to obtain. Read the terms.
LoudReader, the iOS reading app, runs on this engine. loudkit is the engine it ships.