Your terminal, read aloud.
voce (Italian for “voice”, said VOH-chay) reads Markdown to you in the terminal. Each sentence lights up as it is read, and each word as it is spoken. Code is read the way a pairing partner says it, operator names and all. Your place is always kept.
uv tool install git+https://github.com/jaystewartuk/voce
Free and open source · macOS · Python 3.9+
Why the cache misses
The cache key ignores the query string, so /search?q=a and /search?q=b share one entry. Add the query to the key, and keep the TTL short while you test.
pythondef cache_key(request): # Include the query string, sorted so ?a=1&b=2 matches ?b=2&a=1 query = "&".join(sorted(request.query.split("&"))) return f"{request.path}?{query}"
| Request | Before | After |
|---|---|---|
| /search?q=a | hit | miss |
| /search?q=b | hit (wrong) | miss |
- ☑Reproduce it with two queries
- ☐Ship the fix behind a flag
The real thing uses Google's Chirp 3 HD voices or your Mac's own. This uses your browser's voice.
What it does
Reads what a person would read.
Markdown is written for the eye. voce shows it the way it should look and reads only the prose, so you can listen with your eyes closed or follow every word.
Follows along
The sentence being read sits on a pale band, the word being spoken on a darker one, timed from the audio itself.
Reads code like a pairing partner
Highlighted in every language Pygments knows, and read line by line with the real names: "is identical to", "double arrow", "null coalescing", "spaceship".
Tables that line up
Columns are aligned on screen and read one row at a time, cell by cell.
Keeps your place
Quit at any point and carry on from the same sentence, even after the file has been edited.
Free to listen again
Every sentence is fetched once and cached, so going back or listening again costs nothing and works offline.
Take it with you
--export stitches files into one MP3, with the same voice and pauses, for a walk or a drive.
Playlists and quizzes
Point it at a folder's README and listen through in order. Quizzes pause for your answer.
Click to jump
Click any sentence to hear it, or use the arrows to move by sentence, paragraph or section.
Gapless and in time
A tiny native player keeps sentences flowing, pauses mid-word and changes speed without changing pitch.
New
Code, read the way a pairing partner says it.
Every reader that speaks code gets it wrong in one of two ways. Screen readers spell out the
symbols: "dollar, totals, left bracket, dollar, channel". Text-to-speech apps read code as prose, and
Str::slug comes out as "stress log". voce says what the person next to you would say, one line at a
time, and the line lights up as it is read.
On the line
$totals[$channel] ??= 0;fn ($a, $b) => $a['nights'] <=> $b['nights']foreach ($rows as &$row) {'1' === 1; // false: a string is not an integer(int) (19.99 * 100); // 1998, a cent shortStr::slug('Sea View');* @param array|string $columns$reservation->guest?->notify(new BookingConfirmed($reservation));
What you hear
- "totals channel null coalescing assignment zero"
- "arrow function a, b returns a nights spaceship b nights"
- "for each rows as by reference row"
- "the string 1 is identical to 1, false: a string is not an integer"
- "cast to int 19.99 times 100, 1998, a cent short"
- "string double colon slug Sea View"
- "parameter array or string, columns"
- "reservation arrow guest nullsafe arrow notify new Booking Confirmed reservation"
Real names, so you learn them
Operators are called what they are called: arrow, double arrow, double colon, identical, equal, null coalescing, spaceship, nullable, cast, increment, by reference, unpack, variadic. They are the PHP manual's own names. After a few sessions you say them without thinking, which is what a pair-programming interview or a code review asks of you.
Names as words
array_values is "array values", strlen is "string length", usort is "U sort", isset is "is set". A variable's dollar sign, the brackets and the semicolon are silent. A string is read by its contents, and a trailing comment as prose.
Checked by ear, and against Laravel
Every rule is fetched from the real voice, transcribed with whisper and compared with what should be heard, and the rules were read right through laravel/framework to find what came out odd. Written for PHP and Laravel first; most rules carry to any language with the same symbols, and the rest are a line each. Hear it on a reservation sync in Laravel, or prefer the prose alone with --skip-code.
Made for Claude Code
Listen to the reply while you think about it.
Long replies are easier to take in by ear, away from the wall of text. voce reads Claude's latest reply straight from the session, with no copying and pasting.
Ask Claude
Work in Claude Code as usual, in one terminal.
Run
voce -rIn a second terminal, in the same folder, when the reply lands.
Listen
You hear the final message, the same text
/copytakes, without the tool calls or thinking.
> Why does the cache miss?
⏺ The cache key ignores the query string, so…
The cache key ignores the query string, so…
▶ reading · 2/8 · 1.1× · Charon
Prefer copying? /copy then voce -c works too.
Written for the eye, read for the ear
What you see, and what you hear.
The voice never hears markup. Symbols and abbreviations are read as words, and things meant for the eye stay on the screen.
On screen
- Bold, italic,
codeand links - A fenced
pythoncode block | Request | Before | After |- e.g. a → b, ~3 × faster
<details>hints and "Next:" links
Spoken
- "Bold, italic, code and links"
- "array values", "identical", "arrow"
- "Request, Before, After", then each row
- "for example, a to b, about 3 times faster"
- Shown, not read
Voices
Good out of the box. Beautiful with Google.
Start with your Mac's own voices, with nothing to set up. Add an API key for Google's Chirp 3 HD voices, which sound close to a person reading.
Mac voices
Works the moment you install it, and offline. Pick any voice installed on your Mac with --voice.
Google Chirp 3 HD
Eight voices to cycle through with v, in any language Google offers. Set
VOCE_GOOGLE_API_KEY, or sign in with gcloud.
Google's free tier covered a million characters a month when this was written, about 18 hours of listening. voce counts what you use. Setup guide
Keys
Everything is one key away.
Your hands stay where they are. The mouse works too: click a sentence to jump, click code to copy it, scroll to look around.
Install
Ready in a minute.
You need macOS and Python 3.9 or newer. The Xcode command line tools give you the gapless player, and ffmpeg is only needed for MP3 export.
❯ uv tool install git+https://github.com/jaystewartuk/voce
# or with pipx
❯ pipx install git+https://github.com/jaystewartuk/voce
❯ voce -r # Claude Code's latest reply
❯ voce -c # the clipboard
❯ voce notes.md --export notes.mp3
Full documentation is in the README.
Privacy
Your notes stay yours.
No telemetry
voce makes no network calls of its own. There is nothing to opt out of.
Google only if you ask
Text goes to Google's Text-to-Speech API only when you set up Google voices.
Plain files
Your place and settings live in one JSON file, and the audio cache is safe to delete at any time.