Projects Pichi case study

Pichi

Hold Fn, speak, and the text lands at your cursor. A free, fully offline dictation app for macOS. My experiment in rebuilding a Wispr Flow-style tool that never leaves my laptop.

macOS app100% on-deviceEnglish + SerbianPersonal tool · not distributed
Status
In daily use on my Mac
Stack
Swift · AppKit · whisper.cpp · Metal
Model
Whisper small, 5-bit quantized
Runs on
MacBook Air M4 · macOS 13+
Claude Code · promptrefactor the auth middleware and add tests▍

Hold fn → speak → release → text appears

In short

  • WhatA menu bar app. Hold Fn to talk, or press Fn+Space for hands-free. Works in any app.
  • Who forMe, mostly for talking to Claude Code while I work.
  • PrivateThe app makes no network calls. Audio never leaves the Mac.
  • LearnedAudio capture, global key events, macOS permissions and linking a C++ library into Swift.

Why it exists

I talk to coding assistants all day, and speaking is faster than typing. Wispr Flow does this well, but it's a paid subscription and my transcripts would go through someone else's servers.

I wanted to see if I could reverse-engineer the same feel with four rules: free, local, offline, and light enough to run next to my dev tools on a MacBook Air. It's a personal tool, not a product.

From key press to text

965 lines of Swift in 11 files. No third-party Swift packages.

  1. 01 · ListenGlobal hotkeyA CGEventTap watches the Fn key in every app.
  2. 02 · RecordCapture audioAVAudioEngine buffers the mic while Fn is held.
  3. 03 · CleanGate and normalizeDrops clips under 0.3 s or too quiet, resamples to 16 kHz mono, boosts quiet speech.
  4. 04 · Transcribewhisper.cpp on MetalAuto-detects English or Serbian. My own word list steers spelling of tech terms.
  5. 05 · PasteInsert at cursorSaves the clipboard, pastes with Cmd+V, restores the clipboard 0.25 s later.
Hands-free modeFn+Space to start, Fn+Space again to paste, Esc to cancel. A floating "listening" pill shows it's on, and never steals focus.

Key decisions

What I chose, what I gave up for it.

Model

Multilingual "small" instead of English-only

Why: I started with base.en, then switched so I could dictate in Serbian too.

Trade-off: about 3× bigger (181 MB vs 57 MB) and slower. English is solid; Serbian is still a bit messy.

Text insertion

Paste, don't type

Why: one Cmd+V is faster and more reliable than typing character by character, and it avoids autocorrect.

Trade-off: the clipboard changes for a moment, and it can't work in password fields, which macOS blocks on purpose.

Hard problems

It crashed on every Cmd+Q

On quit, the GPU library tore down Metal while my whisper context was still alive, and an assertion killed the app. Now I free the context before exit, on the same queue that runs transcription, so it also waits for any work in progress.

"It stops working when I lean back"

It wasn't frozen. My silence filter was throwing away quiet speech without a sound. I lowered the threshold, added volume normalization (capped at 15× so noise isn't boosted), and a soft "tink" when a clip is too quiet.

How it came together

From a written plan to a tool I use every day.

  1. PlanWrote the PRD: goals, phases, what's out of scope.
  2. SetupSigning setup and an empty menu bar app.
  3. HotkeyFn hotkey, recording and pasting placeholder text.
  4. SpeechFirst real transcription with whisper.cpp, plus a custom word list.
  5. LanguagesEnglish + Serbian auto-detection.
  6. Real useUsed it for real and fixed what broke: quiet speech and the crash on quit.
  7. Hands-freeHands-free mode and the floating indicator.
0.49 sto transcribe an 11 s clip on M4 with Metal*
2.2 MBapp binary, whisper.cpp included
181 MBmodel, stored locally
0network calls in the app

* Measured with the smaller English model during development. I haven't benchmarked the current model yet.

How I worked

Claude Code wrote the code, in a language I don't know. That was the point of the experiment: how far can I get by directing AI well?

My part was the product. I wrote the PRD and split the work into phases, made the privacy-first decisions, and did all the testing on a real machine: holding the key, speaking, and reporting exactly what broke. My other projects are code I wrote myself; this one shows the other side.

What I'd do next

Reliability
  • Fix thread safety in the recorder
  • Re-enable the hotkey if macOS disables it
  • Unit tests for the audio gates and hands-free logic
Speed
  • Measure latency and memory with the current model
  • Stream long hands-free sessions instead of one big clip
Product
  • Settings: microphone, hotkey, word list
  • A pinned, reproducible build
  • Optional AI grammar pass (bring your own API key)
  • Windows version, then open source
See it in action

Pichi runs only on my Mac for now, so the demo video is the best way to see it.

It's a personal tool for now. A Windows version and open-sourcing it are on my list.

Like what you see?

I'm looking for my first role as a junior full-stack or backend developer, remote or in Belgrade. Let's talk.