← All work

An open-source release / Product case study

TTSNative

Natural text-to-speech. Private, powerful, and local.

I designed and built a private document reader that turns PDFs, EPUBs, and Word documents into natural speech on your own computer, with optional consent-based voice cloning.

~1 s
to start reading, even a whole book
54
built-in natural voices
0
accounts, subscriptions, or cloud inference
TTSNative promotional artwork: any document, read aloud
TTSNative product artwork. The walkthrough below is a recording of the running app.

The problem

Natural narration that stays on your computer.

TTSNative reads PDFs, Word documents, EPUBs, Markdown and text aloud with natural voices, generated on your own computer. There are no accounts, no subscriptions and no cloud inference, and your documents and recordings stay on the machine.

It started as Local TTS, a 250-line Gradio script. It worked for plain text and EPUB on my machine, but PDFs silently produced no audio, its dependencies were unpinned, its model downloads weren't verified, and it had no tests, license or CI. Version 2.0 rebuilt it from scratch as a document reader and voice studio.

My role

One owner, from prototype to release.

I came up with the product, designed the experience, developed the application, and own its releases.

Responsibility
Product vision · Design · Engineering · Delivery
Platform scope
A local web app: a React interface and a Python service on your own computer
Current status
Open source (MIT); version 2.0.0 released October 19, 2026

Product decisions

The choices behind the experience.

What I chose, why it mattered, and what I accepted in return.

01

Speech is generated on your computer, and nothing is sent anywhere.

I made TTSNative local-only. It runs on your own machine, works offline after setup, and never sends documents or recordings anywhere. The only network requests are downloads you start, from pinned addresses with checksums.

Why I chose it. Documents and voice recordings are personal. A reader that keeps them on your computer doesn't ask you to trust a server.

The tradeoff. Setup needs a first-run download, about 0.7 GB for the basic reader. And when the voice-cloning engine's locked packages change, the app offers an update rather than downloading packages you didn't ask for.

How it is delivered

The service binds to 127.0.0.1, validates the Host header, requires a custom header on requests that change state, and never enables CORS. Access from another device needs an explicit network mode with an access token. Model downloads are pinned and verified by SHA-256.

02

Playback should start in about a second, even for a whole book.

I made the app synthesize one sentence at a time, on demand, just ahead of the listener, and cache each sentence's audio by its content.

Why I chose it. Nobody should wait for a book to render before hearing it. Because each sentence is its own clip, the read-along highlight is exact, and seeking, switching voices and resuming an export are cheap.

The tradeoff. Rhythm across sentences isn't modeled; pauses are added by the kind of boundary instead. Word-by-word highlighting waits until real word timings are available.

How it is delivered

The browser asks for the current sentence at playback priority and the next three at prefetch priority. Skipping cancels work nobody is waiting for, and exports run at a lower priority than live playback.

03

Voice cloning is optional, and kept apart from the reader.

I put the voice-cloning engine, Chatterbox Turbo, in its own isolated environment, installed only if you choose it in Settings.

Why I chose it. Its dependencies conflict with the reader's, and it is a multi-gigabyte download. The basic reader should never need a GPU or that download.

The tradeoff. Cloning is a separate install, about 8 GB more with NVIDIA CUDA, and there are two Python environments to maintain.

How it is delivered

The cloning worker is driven over a JSON-lines protocol on standard input and output. If it crashes, the server keeps running, and it unloads after 10 idle minutes to free graphics memory.

04

A custom voice starts with consent and ends with deletion.

Voice Studio asks you to confirm that the voice is yours, or that its speaker gave permission, before it creates a voice. Recordings stay on your computer, and one action permanently deletes the recording, the voice and all audio made with it.

Why I chose it. A cloned voice can be used to impersonate someone. The person whose voice it is should stay in control of it.

The tradeoff. No cloned voices ship with the project, so everyone records or imports their own. A published voice could be used by anyone to impersonate its speaker.

How it is delivered

Quality checks run as you record and again on the server, which also requires the consent confirmation. Audio generated with a cloned voice carries Resemble AI's inaudible Perth watermark.

05

The website's demo says whose voice you're hearing.

Visitors to the website can try speech in their browser. The demo uses the browser's own voices, says plainly that they aren't TTSNative's, and flags online voices, which some browsers send to a cloud service.

Why I chose it. The website promises no cookies, analytics or third-party requests. Running TTSNative's voice model in the browser would mean downloading about 90 MB or more per browser from a model host.

The tradeoff. The demo sounds different from the app, so the page says so.

How it is delivered

The demo uses the browser's Web Speech API. I'll revisit running the real model in the browser when a small enough model, with a phonemizer whose license allows it, can be hosted on the site itself.

Product walkthrough

From a document to narration you can follow.

A two-minute recording of the running app with its real narration. The steps below explain the choices behind the flow.

Recorded from the production build with the real models. The cloned voice in the Voice Studio step is synthetic: its reference is one of the built-in voices, not a real person.

  1. Add a document

    Open a PDF, Word document, EPUB, Markdown or text file, or paste text. Chapters come from the outline, the table of contents or the headings. A scanned PDF with no text is reported rather than silently skipped.

  2. Choose a voice and press Play

    Pick from 54 built-in voices, with quality grades and side-by-side comparison on your own passage. The text is highlighted sentence by sentence, and the app remembers where you stopped.

  3. Make a voice of your own, if you want one

    Voice Studio makes a voice from a 10–20 second recording or an existing clip, with live quality checks and explicit consent.

  4. Export to listen anywhere

    Save the whole document or chosen chapters as WAV or MP3, as one file or one per chapter. An interrupted export resumes from the audio already made.

Watch the demo and the 45-second overview

Improving the studio voice

I tested whether recording more helps.

Voice Studio's guided studio session asks for 48 passages: about 30 minutes of recording. That is a lot to ask, so I traced what the voice engine actually uses and ran a blind listening test of six voices on ten new items.

Before

48 suggested passages. The complete voice used the audio of only 14 of its 42 takes, and its averaged speaker embeddings settle after about 8–12 takes.

After

About 15 suggested passages, with the most useful one to record next. The voice tiers say what they mean, and that no model is trained.

What the test found. A voice from 13 takes beat a quick clone on 9 of 10 items (p = 0.02). The complete voice, from 42 takes, was no better than that early one. The careful recording setup made the difference, not the length.

These app changes are not in a release yet. The test was one listener and ten items: a careful first test, not a study.

Read the studio voice case study

Engineering depth

What sits behind the interface.

I built the application and its tests alongside the product. These figures describe the scale of that work.

~20k
lines of application source
~12k
lines of tests and test support

Source snapshot · Oct 20, 2026. Physical lines include comments and blanks. Dependencies, generated output, assets, and lockfiles are excluded.

CI coverage report · Oct 20, 2026: 100% of statements and branches in both the web app and the server. CI requires 100% of statements, branches, functions and lines in both. Coverage measures unit and component tests, which use a fake engine instead of real models, and neither app has coverage-ignore comments.

Performance on one machine · Oct 19, 2026
MeasurementResult
First sentence after the model loads (~70 characters)1.06 s
Median sentence (~70 characters)0.88 s
A 100-sentence document9.2 minutes of audio in 117.5 s, about 5× faster than listening
A cached sentence over HTTP (median of 20)71 ms
Extracting a 300-page text PDF, sandboxed2.25 s
Chatterbox Turbo on the GPUReal-time factor 0.58

Windows 11, an AMD Zen 3 desktop CPU with 32 logical cores, 64 GB of RAM and an NVIDIA RTX 3080. Kokoro runs on the CPU only. No other hardware was measured.

What the tests cover, and what they don't
  • Unit and component tests: Vitest for the web app and pytest for the server, with a fake engine so no model is loaded.
  • Real-model tests run Kokoro, and Chatterbox when it is installed, through the API.
  • Playwright plays real Kokoro audio in Chromium, Firefox, WebKit, and Pixel 7 and iPhone 13 emulation, with axe accessibility scans.
  • Outside those numbers: the Chatterbox worker (about 360 lines) and the Node scripts (about 4,000 lines) have no unit tests, and subjective audio quality isn't tested automatically. That is why the studio voice was tested by ear.
  • Read the testing guide.

Delivery scope

What I brought together.

  • A reader for PDF, Word, EPUB, Markdown and text
  • Read-along playback with on-demand synthesis and caching
  • A library of 54 voices with side-by-side comparison
  • Optional Voice Studio with consent, quality checks and deletion
  • Export to WAV or MP3, by document or chapter
  • One-command setup with checksum-verified downloads

React · TypeScript · FastAPI · SQLite · Kokoro · Chatterbox Turbo

A closer look at technical delivery
  • A React and TypeScript interface talks to one local FastAPI service, with API types generated from the server's schema.
  • Documents are parsed in a separate process with a timeout. Malformed archives and XML entity declarations are rejected before any parser runs.
  • There is no language model in the pipeline: document text is read aloud, never interpreted, which removes the prompt-injection surface.
  • Releases are checked against the changelog and ship the source, a prebuilt web app, SBOMs, checksums and signed build provenance.
  • The rename to TTSNative came with a new identity: an N drawn as five waveform bars, with app icons, logos and a brand guide.

Results and status

Released, open source, and still improving.

TTSNative 2.0.0 was released on October 19, 2026, under its earlier name, Local TTS: a complete rebuild of the original prototype. Since then I've renamed it TTSNative, with a new identity and a new website. The source also adds studio voices, a pronunciation dictionary, bookmarks and notes, and search inside documents; those aren't in a release yet.

TTSNative has no accounts or analytics, so this case study reports no usage figures. The full app was tested on Windows 11. On macOS and Linux, CI runs the server tests, but the full app wasn't tested by hand. Safari was tested with Playwright's WebKit build, not on Apple hardware.

What this work represents

A private app has to keep its promise everywhere: in setup, in updates, in voice cloning, and even in the website's demo. Owning the whole product meant making each of those choices hold to it.

Next case study

PicPledge

I designed and built an event photo-sharing product, then launched it at my own wedding.