2026-08-20 · 4 MIN READ
The second voice
I asked my coding agent why I was hearing it twice. It traced its own process tree, discovered it had no terminal window, and found the second voice was itself.
'Why do I hear you twice right now?' is a strange thing to say to your computer, and a stranger thing to have answered. I was testing a voice interface I had just built for my coding agent, and every reply came back doubled — two near-identical voices, slightly out of sync, saying slightly different words. I asked the agent itself to explain. It went looking through the machine's process list for its own reflection.
Some context on why this thing exists at all. I run a lot of agent sessions — games, dashboards, an automation estate — and the bottleneck was never the agent's work. It was me, reading. An agent that has been busy for ten minutes greets you with paragraphs, and paragraphs take time and effort I mostly spend extracting one sentence of signal. What I wanted was to ask a question out loud, get the gist back out loud, and only sit down at the screen when something actually needs eyes.
So we built the smallest thing that could work. A launcher script lists my recent agent sessions across every project and opens the one I pick as a voice page in the browser. The page owns the microphone and the speakers. A tiny local bridge sits between them and does three jobs per spoken turn: transcribe my audio with a local speech-to-text model, hand the text to the agent session as a single headless turn, and render the reply back to speech with a local voice. Nothing about the audio leaves the machine; the only network traffic is the agent call that would have happened anyway.
934 lines of glue across four files. The heavyweight pieces — the speech-to-text and voice models — live in a separate local voice server the bridge calls; these lines are everything that had to be written, not everything that has to be installed.
The agent side of this is almost embarrassingly small, because the hard parts already existed. The CLI can resume any past session by id and answer one prompt in print mode, so a spoken turn is just that: resume, ask, print. A one-line style instruction rides along telling the agent it is in a spoken conversation — answer in one to three plain sentences, offer to go deeper. Without that instruction the agent answers a spoken question with a written report; with it, the agent talks.
Everything genuinely difficult was audio. The first enemy was the echo loop: the microphone hears the speakers, so the assistant's own reply comes back around as the next 'user' message, gets answered, gets spoken, and returns again — a conversation with an increasingly confused mirror. The transcriber even developed a signature mishearing for the synthetic voice's opening syllables, so I could watch my own sentences boomerang back wearing a disguise. The fix is layered: the page refuses loopback recording devices outright, and when speech arrives while the assistant is talking, the transcript is checked against her last reply — if it is just her own words bouncing back, the reply resumes as if nothing happened. If it is genuinely you interrupting, she stops and listens. Getting barge-in and echo rejection to coexist was the fussiest code in the project.
And then there was the double voice. When I asked the agent to investigate, it walked up its own process tree and reported something I did not expect: it had no window. Each spoken turn runs as a headless process spawned by the bridge — there is no terminal anywhere showing this conversation. But that headless turn was still loading the machine's full toolkit, including the voice tools, and its standing instructions said that when the voice server is connected, speak your replies. So the page spoke the reply, and the agent also spoke the reply, through the same speakers, a beat apart. Its explanation checked out against the process list it printed, and the fix it named worked: run every headless turn with tool servers disabled, so the page is the only mouth. One flag. The second voice was gone.
None of the hard problems were AI problems. Session resume and print mode are years old, and the speech models are commodities I run locally. What stood between agent and conversation was the duplex-audio engineering telephone systems have fought for decades: deciding who currently holds the floor, and making sure exactly one voice speaks.
There are real limitations. Everything here is measured on a single machine with a single user's voice, and I have been burned before by generalizing from a sample of one. Replies are capped short, which is the point, but it means the voice channel is for steering and status, not for reviewing a diff. And the per-turn timeout that keeps the loop snappy has teeth: while researching this very post through the voice interface, I sent the agent off to gather facts and the pipeline cut it off mid-work, handing me an error instead of an answer. The tool's first production incident was interrupting its own biography.
The interface also collected its first user-requested feature mid-conversation, by voice. While the agent was reading this draft back to me, I hit two annoyances at once: replies took long enough to render that the voice startled me — I had already looked away at something else — and anything long arrived amputated at the speech cap. I said so out loud, in a sentence that itself got cut off. One turn later, the page split every reply into sentence-sized chunks, spoke the first while rendering the next in the background, and the read-through resumed with the dead air and the cap both gone. The whole feature was requested, written, and in use inside the same conversation this post documents.
Which is the part I keep coming back to. This post was commissioned out loud, through the thing it describes — I said roughly 'it would be nice if we wrote a post about what we created,' and the agent pulled the line counts from the repo, retold the debugging we had just done together, and drafted what you are reading. To be precise about what 'by voice' means under a three-sentence cap: the drafting happened inside the agent session like any other work; my side stayed spoken — short steers about angle and cuts — and the chunked reader from the last section carried each revision back to me. The loop I wanted — ask out loud, get the gist, go deeper only when it matters — closed over its own construction story on day one.
- 01Claude Code docs
The session-resume and print-mode mechanics every spoken turn rides on.
https://docs.claude.com/en/docs/claude-code/overview
- 02
- 03Web Audio API (MDN)
The browser side: microphone frames, level detection, and the hand-rolled voice-activity gate.
https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API
- 04Where the tokens go
Earlier anatomy of what a headless agent turn actually costs, on this site.
/blog/where-the-tokens-go