Skip to content
← Back to field notes
AI/LLM8 August 20265 min read

OpenClaw Episode 5: I Set Up Local Text-to-Speech on a MacBook Air. Here Is What I Learned.

Getting TTS running locally on a 2015 Intel MacBook Air - the mistakes, the model swaps, and what actually works.

Most AI text-to-speech guides tell you to call a cloud API. That works until you care about latency, cost, privacy, or offline access. This is the story of getting TTS running locally on a 2015 Intel MacBook Air - the mistakes, the model swaps, and what actually works.

The starting point: cloud TTS is easy but limiting

My AI assistant (running on OpenClaw) needed voice output for Telegram messages. The default path was cloud TTS - pick a provider, send text, get audio back. Simple.

But I wanted something different. I wanted the whole thing to run on my machine. No API keys, no per-character pricing, no network dependency. Just a local model that turns text into speech.

The reasons were practical:

  • Latency. Cloud round-trips add 1-3 seconds. Local inference on Intel is not fast, but it is predictable.
  • Cost. TTS APIs charge per character or per minute. For a voice-first interface, that adds up fast.
  • Privacy. Some messages should not be sent to a third-party API.
  • Control. I wanted to swap voices, models, and languages without being rate-limited.

Attempt 1: Piper (English-only, lightweight)

The first install was sherpa-onnx with the Piper en_US lessac voice model. This is a VITS-based model trained on English speech. Lightweight, fast, decent quality.

Setup was straightforward:

  1. Download the sherpa-onnx runtime (prebuilt binaries for macOS)
  2. Download the Piper model (about 60MB)
  3. Point the wrapper script at the runtime and model directories
  4. Call the CLI with text, get a WAV file

It worked. English sounded natural enough for quick voice replies. The lessac voice is calm and clear - a good default for an assistant.

But there was a problem. I am Malaysian. Like many Malaysians, I grew up switching between languages mid-sentence. Cantonese, Mandarin, English - sometimes all three in the same conversation. It is not code-switching as a gimmick. It is just how we talk to friends, family, and colleagues. My AI assistant needed to do the same.

When I sent Cantonese text to the Piper model, it tried to phonetically read Chinese characters using an English voice engine. The result was unlistenable.

Attempt 2: Kokoro multilingual (what I run now)

I needed a model that could handle both English and Chinese. After researching the sherpa-onnx model zoo, I found Kokoro multi-lang v1.1.

Kokoro is a different architecture from Piper. It supports 103 voices across multiple languages including Chinese (Mandarin and Cantonese-leaning pronunciation), English, Japanese, and Korean. The model is larger (about 320MB) but still manageable on a 4GB RAM machine.

The key specs:

Piper lessacKokoro multi-lang v1.1
LanguagesEnglish onlyEN, ZH, JA, KO
Voices1103 (selectable via speaker ID)
Model size~60MB~320MB
ArchitectureVITSKokoro (style-based)
QualityGood for EnglishGood across languages
OfflineYesYes

I picked speaker ID 59 (zm_010), a Chinese male voice. It handles English cleanly and produces intelligible Chinese. Not perfect Cantonese, but far better than the Piper fallback.

The wrapper problem

Installing Kokoro was not plug-and-play. The sherpa-onnx TTS wrapper script was built for VITS-style models (Piper). It passed --vits-* flags to the runtime. Kokoro needs --kokoro-* flags. Different parameter names, different model loading logic.

When I first ran Kokoro through the unmodified wrapper, the output was garbled noise. The model was being invoked with the wrong parameters.

The fix was a patch to the wrapper's auto-detection logic: check whether the model directory contains voices.bin (a Kokoro-specific file). If yes, use Kokoro flags. If not, fall back to VITS flags. About 30 lines of code.

After the patch:

  • English voice output: working
  • Chinese voice output: working
  • End-to-end OpenClaw TTS integration: working

What I would do differently

If I were starting fresh, I would skip Piper entirely and go straight to Kokoro. The extra 260MB is worth it for multilingual support alone. On a single-language English project, Piper is fine. On anything that touches Asian languages, Kokoro is the better default.

I would also test with mixed-language input from day one. Pure English tests passed every checkpoint. The real failure mode (Cantonese through an English model) only showed up in production.

The current setup

The final configuration:

  • Runtime: sherpa-onnx v1.13.2 (macOS universal binary)
  • Model: Kokoro multi-lang v1.1
  • Voice: Speaker ID 59 (zm_010, Chinese male)
  • Integration: OpenClaw TTS tool calls the patched wrapper, which auto-detects the model type and passes the correct flags
  • Performance: ~1-2 seconds for short phrases on Intel i5. Acceptable for voice replies.

No API keys. No cloud calls. No per-character billing. Just a local model that speaks two languages on a 5-year-old laptop.

Local TTS is not better than cloud TTS in raw quality. The best cloud models (ElevenLabs, Azure Neural, Google WaveNet) sound more natural. But local TTS wins on cost, privacy, latency consistency, and the satisfaction of running the entire pipeline on your own hardware.

If you are building a voice interface and have not tried local models yet, sherpa-onnx with Kokoro is a good place to start.