New · LinnetTTS

Text-to-speech that runs on the device

Give your product a natural voice that starts speaking in a fraction of a second, with no cloud, no network, and no per-character bill. LinnetTTS streams audio as it generates it and keeps up in real time on a single phone CPU core.

A small 3D linnet songbird singing, with sound waves coming from its beak
Live demo · runs in your browser

Type anything and hear it, generated on your own CPU

This is the real model running in this tab through WebAssembly. Nothing you type leaves the page, and no server generates the audio. The first run downloads the 83 MB model once, and your browser keeps it for next time.

Or listen right now, with no download

English only, and write numbers as words for the best result. The browser build was made for this demo and uses about 350 MB of memory, more than the native SDKs need. Speed depends on your device.

Capabilities

What you get

Streams from the first step

Audio arrives in chunks of about 80 ms, so speech starts right away instead of after a whole sentence renders.

Real time on one core

7% of real time on an iPhone 13 Pro Max and 53% on a budget Android phone, on a single CPU core.

Two voices included

A female and a male American English voice, both synthetic, and both free to use as your product's voice.

Custom voices on request

The model speaks in the voice given to it as a short prompt, made from 5–6 seconds of clean speech. We create brand voices under a separate license.

Fully on-device

Works offline, keeps text private, and has no per-character or per-user fees however much your product talks.

Plain text in, 24 kHz out

No phonemizer or pronunciation dictionary to bundle. The whole text frontend is a 102 KB tokenizer.

Overview

A voice for your product, with no server behind it

A cloud voice adds a network round trip to every sentence, stops working when the connection drops, and sends a bill that grows with usage. LinnetTTS removes all three. It generates speech on the device your product already runs on, so spoken responses, navigation prompts, and read-aloud features work anywhere, and the text stays with your user.

Pair it with our speech recognition and wake word and you have a complete voice loop, listening and speaking, with no cloud in the path.

How it works

Prime the text, then play chunks as they arrive

Every platform exposes the same three objects: a model, a voice, and a streaming session. Prime a session with the text, and each step returns about 80 ms of 24 kHz mono audio to hand to your player. A session covers up to 24 seconds by default, so split longer text into segments. The weights, tokenizer, and voice are separate downloads.

Read the docs →

// Android
val model = LinnetModel(File(filesDir, "linnet-en-35m.fp16.safetensors"),
                        File(filesDir, "tokenizer.model"))
val voice = model.loadVoice(File(filesDir, "linnet-en-f1.npy"))

model.session(voice, SynthParams(seed = 0)).use { session ->
    AudioTrackPlayer(model.sampleRate).use { player ->
        session.prime("Your package arrives tomorrow.")
        while (true) {
            val chunk = session.step() ?: break  // about 80 ms of audio
            player.write(chunk)
        }
        player.drain()
    }
}
Under the hood

Built small on purpose

LinnetTTS is an autoregressive flow language model that emits one continuous latent per 80 ms step, using a single flow evaluation per step, followed by a Vocos-style neural codec decoder. Every operator is causal, with no lookahead anywhere in the chain, which is what makes true streaming possible.

It runs on a from-scratch Rust runtime that contains only the kernels this model uses, with NEON and AVX2+FMA fast paths. There is no ONNX Runtime or PyTorch underneath, and on Linux the library needs nothing beyond the standard system libraries. The code, including the runtime, is open source under Apache-2.0.

Performance

Measured on real devices

Real-time factor is the time spent generating as a share of the audio's length, so lower is better and anything under 100% keeps up with playback. Rust runtime, fp16 weights.

DeviceThreadsPer 80 ms stepReal-time factor
iPhone 13 Pro Max (A15)1 / 25.5 / 4.6 ms7% / 6%
iPhone 11 (A13)16.7–7.1 ms8–9%
Snapdragon 662, Redmi 9T (budget Android)1 / 442.4 / 14.3 ms53% / 18%
Raspberry Pi Zero 2 W (Cortex-A53, 1 GHz)1 / 4158 / 59–60 ms198% / 75%
Apple M5 (macOS)13.3 ms4%
Desktop x86-64, Core i9-13900K (AVX2)1 / 47.0–7.4 / 2.5 ms9% / 3%

Measured without real-time pacing. When synthesis is paced to playback, mobile power management raises the step time 3–4×, which is why the sample apps generate in a burst and let playback drain behind it. First audio arrives 50–130 ms after the text on iPhones. In the Android sample app on the Snapdragon 662 it takes about 250 ms once warm, and 300–430 ms for the first utterance. The Raspberry Pi Zero 2 W needs all four cores to stay under real time. Source: the model card.

How it compares

The lightest in memory, and it streams inside a sentence

We ran LinnetTTS against three popular CPU text-to-speech models, each through its published package on the same desktop machine. LinnetTTS uses the least memory of the five and starts speaking well before a sentence is finished, which Piper and Kokoro do not do.

ModelPeak memoryWeights on diskStreamingReal-time factor, 1 / 4 threadsFirst audio, short / long textVoice cloning
LinnetTTS187 MB83 MBEvery 80 ms10.6% / 3.4%87 / 133 msYes, separate license
Piper medium503 MB64 MBPer sentence5.8% / 2.1%180 / 283 msNo
Piper high639 MB114 MBPer sentence42.5% / 12.4%1.4 / 2.1 sNo
pocket-tts 4.0.1 (Kyutai)968 MB236 MBEvery 80 ms23.8% (2 cores) / 24.1%71 / 82 msYes
Kokoro-82M v1.0 (ONNX fp32)1246 MB326 MB + 28 MB voicesNo51.7% / 17.2%1.5 / 9.6 sNo

Measured by us on 2026-11-04 on a Core i9-13900K under Linux (WSL2), with Python driving each model and the Piper en_US-ljspeech voices. This compares speed and footprint, not voice quality, and desktop numbers don't predict phone numbers. Real-time factor here includes text processing and the Python loop, so Linnet reads higher than in the table above. Peak memory is the whole Python process, including its framework. The table measures English synthesis only. Method and commands to reproduce it, and the same sentence from every system for listening.

Supported devices

One model on every platform

The same model, voices, and streaming session everywhere, each with install instructions and a sample.

PlatformMinimum OSCPU architecturesDistribution
AndroidAndroid 7.0 (API 24)arm64-v8a (NEON); x86_64Kotlin AAR on the GitHub release, with a sample app
iOSiOS 15arm64; simulator slicesSwift Package Manager, with a SwiftUI sample
Linuxglibc 2.17+x86_64 (AVX2+FMA); aarch64 (NEON)C / C++ tarball (.so, .a, header, examples)
PythonPython 3.9+; Linux (glibc 2.17+) or macOS 11+Linux: x86_64, aarch64. macOS: Apple silicon onlypip install linnet-tts
RustRust 1.85+Any of the aboveGit dependency on the linnet-tts crate
Footprint

What it costs on disk and in memory

  • Model weights on disk (fp16)83 MB
  • Tokenizer + one voice~120 KB
  • Native library, Android arm64-v8a~760 KB
  • Added to an iOS arm64 app binary~2 MB
  • Memory while speaking (weights + session)~105 MB
  • Peak memory, measured on Linux~160 MB

The honest trade-offs: v1 is English only. A slight roughness is audible on headphones in some utterances, though not on laptop speakers. The model has no text normalization, so convert prices, dates, and phone numbers to words first. An integer variant for weaker devices is planned for a later release. Evaluate it on your own content, since words can occasionally be misread or dropped, and it is not certified as the sole channel for safety-critical or emergency information.

Licensing

Free to ship with the built-in voices

The code, including the runtime, is Apache-2.0. The weights, tokenizer, and voices are under the Linnet Weights License, which is not an open-source license. In short:

Free, including commercially

  • Using the unmodified weights with the built-in voices inside your own products, on devices and on servers
  • Paid and closed-source products
  • Speech as part of your product, such as read-aloud, assistants, and notifications
  • Shipping the weights with your product

Needs a separate license from VoxRT

  • Creating or cloning voices
  • Modifying or fine-tuning the weights
  • Training other models with the weights or their audio
  • Offering general-purpose text-to-speech to others, in any form
  • Distributing the weights on their own

Conditions apply, including attribution and shipping a copy of the license. This is only a summary: the Linnet Weights License governs. See also Licensing.

FAQ

On-device text-to-speech, answered

Does LinnetTTS send text or audio to the cloud?

No. Text goes in and audio comes out on the device, with no network connection needed. The demo on this page works the same way: after the one-time model download, everything runs in your browser tab.

How fast is LinnetTTS on a phone?

On one CPU core it uses 7% of real time on an iPhone 13 Pro Max, 8–9% on an iPhone 11, and 53% on a budget Snapdragon 662, or 18% with all four big cores. First audio arrives 50–130 ms after the text on iPhones. In the Android sample app on the Snapdragon 662 it takes about 250 ms once warm, and 300–430 ms for the first utterance.

What languages and voices does LinnetTTS support?

English, with two built-in voices: one female and one male, both American English. Both are synthetic and designed from a text description, so they are not clones of any real person. Other languages are not supported by this model; additional languages are customer-driven.

Can I use my own voice or a custom brand voice?

Yes, under a separate license. The model speaks in the voice given to it as a short voice-prompt latent, made from about 5–6 seconds of clean speech from one speaker. VoxRT produces those latents from a recording; the encoder is not published, and the standard license covers only the built-in voices and voices VoxRT has authorized in writing. Talk to us to scope a custom voice.

Is LinnetTTS free for commercial use?

Yes, inside your own products with the built-in voices. The code, including the Rust runtime, is Apache-2.0, and the weights are under the Linnet Weights License. Custom voices, modified weights, training other models, and offering general-purpose text-to-speech to others need a separate license. The license text governs.

How does LinnetTTS compare with Piper, Kokoro, and pocket-tts?

In our CPU benchmark, LinnetTTS uses the least memory of the five models tested and streams inside a sentence, which Piper and Kokoro do not. Piper medium is faster and has smaller weights but has no voice cloning, and pocket-tts reaches first audio slightly sooner while using about five times the memory. The benchmark measures speed and footprint, not voice quality. See the numbers.

How does LinnetTTS handle numbers and dates?

The model has no text normalization. Simple digit forms are usually read correctly, but dense raw text such as prices with decimals, percentages, dates, phone numbers, and version strings is sometimes misread or cut short. Convert those to words before synthesis.

Which platforms does LinnetTTS support?

Android 7.0+ (API 24), iOS 15+, Linux on x86-64 and aarch64 through a C library, Python wheels, and a Rust crate. Windows is not shipped today, and neither is a browser SDK; the browser build on this page was made for this demo, and a browser release is planned.

Give your product a voice

Tell us what it needs to say, and which devices it has to run on. Custom voices start here too.

Talk to us → View on GitHub