I've been shipping software for a while now without writing much about it. That's changing here.
This page is where I'll keep the research — voice AI architectures, editorial UI decisions, and the quiet problems that don't make it into README files. Short notes when I have something useful to say, longer essays and deep dives when the problem is worth unpacking.
What to expect
A few things I'll keep publishing about, roughly:
- Voice-agent internals. How real-time voice systems actually hold together — streaming, tool calls, latency budgets, fallback paths. The parts that aren't in the demos.
- Shipping trade-offs. Decisions I'd make again, decisions I wouldn't, and why. Small receipts from products I've shipped end-to-end.
- Interface craft. The difference between UI that looks done and UI that is done — mostly about motion, type, and the parts users feel before they can name.
What this is not
Not a newsletter. Not a growth channel. Not lorem-ipsum thought-leadership that overpromises and underdelivers.
If a post isn't useful to someone building the same thing, I won't publish it.
A code block, because why not
Here's the kind of thing you'll see when a post has technical meat:
// Streaming a voice agent response, trimmed for readability.
async function stream(prompt: string) {
const session = await voice.open({
model: "realtime-voice-latest",
tools: [bookAppointment, lookupAccount],
});
for await (const event of session.send(prompt)) {
if (event.type === "tool-call") await handle(event);
if (event.type === "audio") speaker.play(event.chunk);
}
}That's it for the intro. The next post will have something concrete.
If you're working on something voice-AI-shaped and want to compare notes, the email link at the bottom of every page goes straight to me.