Phase 4 of my “JARVIS OS” project: a free neural voice that runs on my own server.
Since Phase 2, my morning brief has been read aloud by the stock iPhone voice. It worked, but it sounded like a GPS reading headlines. Phase 4 replaces it with a neural voice that runs on my own server: no cloud speech service, no per-minute bill, and no private text leaving the house.
What it does
Every morning, with no input from me:
- 7:00 AM: the main server builds the brief.
- 7:05 to 7:12 AM: the health reports arrive and the dashboard page is rebuilt (Phase 3).
- 7:15 AM: the server turns the brief text into an audio file.
- 7:45 AM: my iPhone downloads the audio and plays it.
latest.txt --make_audio.py (Kokoro, CPU)--> latest.wav --iPhone Shortcut--> speaker
The stack (still free)
- Speech engine: Kokoro, an open-source model with 82 million parameters, Apache-licensed, running on the CPU
- Glue: one Python script, run by cron
- Playback: an iOS Shortcut with Get Contents of URL, then Play Sound
- Hosting: the same read-only page as before, reachable only on my LAN and VPN
No GPU is needed. A 68-second brief takes about a minute to generate.
Picking the voice
I made samples of four British male voices from a real brief, not a demo sentence, and listened on my phone and PC. Two had the “assistant” feel I wanted. I picked one and kept the other as a fallback Shortcut. I also tried Piper, another free local engine. Kokoro sounded better to me.
I looked at cloud voices too, including Google’s, which has a free monthly allowance, and Gemini’s. I decided against them for one reason: the brief is built from my private data, and I don’t want that going to a free-tier service.
Making it sound right
Most of the work was fixing how the text is spoken, not changing the model.
- Normalize the text first. Times become words (“twelve forty-five P M”), dollar amounts become “5.6 billion dollars,” decimals get “point” (the engine read “9.8” as “9 8”), and colons become commas, because a colon made the voice stop dead.
- Split it like a person would. Long lines are cut at sentence ends. Abbreviations like “U.S.” and “Dr.” aren’t treated as the end of a sentence. Each headline gets its own beat, and a lead-in like “In AI news” flows into the first item.
- Respell the troublemakers. A short list covers words the voice gets wrong, such as “schedule.” I add to it every time I hear a miss.
- Speed. The default felt slow. I tested 1.1, 1.2 and 1.3 on my own phone and settled on 1.3. Pauses shrink as the speed goes up.
Design choices that mattered
Never play yesterday’s brief
If the brief text is more than a few hours old, the script refuses to run. If the engine fails, the old audio file is deleted and I get one generic phone alert. A stale file can’t pass for today’s.
Keep private text local
The brief is spoken on my own server. Nothing in it goes to any speech service.
Test on your own phone
My first Shortcut “worked” in the editor preview, but the preview isn’t a play step. It needed a Play Sound action. I only found that by testing on the phone.
Where it stands
It has played on my phone the last few mornings without a problem. The voice is much better than the stock one, but it’s still a bit one-note on long sentences. That’s the limit of a small free model, and a fair trade for keeping everything at home.
What I’d tell you if you build this
- Audition voices with your real text.
- Fix the text before blaming the model.
- Test the playback step on the device you’ll actually use.
- Make failure loud and stale audio impossible.
What it costs
Nothing. The speech engine, the script and the hosting all run on my own hardware.
What’s next
- A “Brief audio” row on the dashboard, so a failed or stale audio file shows up on the page.
- An interactive dashboard, so I can tap a server and see its guests.
- Per-VM watching, including my Windows VMs.
- Smarter calendar alerts, notes, and a phone-call version of the brief.
If you want a cleaned-up copy of the script, get in touch.