voce
New code is read the way a pairing partner says it

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
View on GitHub

Free and open source · macOS · Python 3.9+

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 short
  • Str::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.

  1. Ask Claude

    Work in Claude Code as usual, in one terminal.

  2. Run voce -r

    In a second terminal, in the same folder, when the reply lands.

  3. Listen

    You hear the final message, the same text /copy takes, without the tool calls or thinking.

~/project ❯ claude
> Why does the cache miss?
⏺ The cache key ignores the query string, so…
~/project ❯ voce -r
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, code and links
  • A fenced python code 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.

built in

Mac voices

Works the moment you install it, and offline. Pick any voice installed on your Mac with --voice.

recommended

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.

spacepause or carry on
←→sentence
↑↓paragraph
[]section
+-speed, 0.8× to 2×
npnext or previous file
vnext voice
mplaylist menu
rhear it again
ccopy the sentence
qquit, keeping your place

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.

# with uv
❯ uv tool install git+https://github.com/jaystewartuk/voce
# or with pipx
❯ pipx install git+https://github.com/jaystewartuk/voce
❯ voce README.md # any Markdown file
❯ 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.