Talk into your computer. Get text where the cursor was. That's the whole product.
Dictado is a tray-resident dictation utility that wraps OpenAI Whisper
behind a single global hotkey. The model is loaded into RAM at login and
stays warm, so each dictation is as fast as the model itself: hit the
hotkey, speak, hit it again, the transcription is on your clipboard and
(if you let it) auto-pasted where your cursor was.
It runs on Windows, macOS, and Linux from the same Python package. There is no cloud, no telemetry, no account. After the one-time model download from OpenAI's public CDN, no audio or text leaves the machine.
Dictation tools either live in the cloud (Otter, Whisper.cpp Cloud, Wispr Flow) or they're locked into a single editor (VS Code voice plugins, Slack's web-only "voice notes"). Neither matches the workflow of "type into whatever has focus, including the terminal, including a GitHub PR description, including Slack's iframe-embedded message box".
Dictado is small enough that you can audit the entire codebase in an afternoon, runs entirely on-device, and uses only the OS primitives your endpoint protection software already trusts (more on that below).
- Agent Input Mode (new in 0.4): turn dictation into one-shot message-sending. Pick a target app from the tray menu and Dictado will activate it, paste, and press Enter for you. Works with ChatGPT, Claude, Cursor, VS Code, Slack, Teams, Discord, Telegram, and more. Full design notes in docs/AGENT_INPUT_MODE.md.
- Configurable global hotkey. Default is Alt + T. Change it from the tray menu without restarting; the choice persists across reboots. Five presets ship out of the box and a Set custom… dialog accepts any combination matching the hotkey grammar.
- Live recording window. A small frameless popup near the bottom of your screen shows a real-time audio-level meter and the transcription as it grows. The popup is borderless and configured to never steal focus, so the cursor stays exactly where you put it.
- Auto-paste, optional. When transcription finishes, Dictado places the text on the clipboard and synthesizes a single Ctrl+V (Cmd+V on macOS) into the window that was active when you started recording. Same primitive every clipboard manager uses. Disable from the tray if you want pure clipboard-only behaviour.
- Audio archive. Each recording is saved as a 16 kHz mono WAV next
to a rolling weekly transcript log (Markdown). Filename is
<datetime>__<firstword>__<lastword>.wav. The archive lives in~/Documents/Sound Recordings/by default and is excluded from git. - Picks the right model for your hardware. Tray menu lets you swap
between
base,small,mediumat runtime.baseis fast enough for live partial previews on a laptop CPU;mediumis the right pick for one-shot dictation when accuracy matters more than realtime.
You need Python 3.10 or newer.
git clone https://github.com/<you>/Dictado.git
cd Dictado
pip install --user .The first launch downloads Whisper weights to ~/.cache/whisper/. After
that there's nothing on the wire.
The fastest way to start the daemon is the platform-specific launcher in the repo root. Same end result as a packaged executable, with none of the PyInstaller bloat:
| Platform | File | What to do |
|---|---|---|
| Windows | Dictado.cmd |
Double-click it. Pin a shortcut to your taskbar for a one-click experience. |
| macOS | Dictado.command |
chmod +x once, then double-click. Right-click → Open With → Terminal on the very first run so Gatekeeper lets it through. |
| Linux | dictado.desktop |
Copy into ~/.local/share/applications/ and chmod +x. It then appears in your application menu like any other app. |
Each one finds a Python 3.10+ on PATH, prefers the sibling .venv
when one is present, and hands off to python -m dictado. The files
are short and heavily commented if you want to tweak them.
dictado # foreground; logs to stdout AND ~/...local/share/dictado/daemon.log
python -m dictado # equivalentA microphone icon shows up in the system tray. Wait for it to turn green and you're ready.
Why no
.exe/.app? Whisper + PyTorch + ffmpeg combined are ~2 GB once frozen. The launcher scripts keep the install lean and usepythonwout of the system-wideProgram Files\Python313— the path your endpoint-protection software already trusts.
To make Dictado start at login:
dictado --install-autostart # OS-appropriate autostart entry
dictado --uninstall-autostart # back outThe autostart mechanism varies per OS:
| OS | What gets created |
|---|---|
| Windows | dictado.lnk in your Start Menu Startup folder |
| macOS | ~/Library/LaunchAgents/io.github.dictado.daemon.plist |
| Linux | ~/.config/autostart/dictado.desktop (XDG-standard) |
If you're on Linux Wayland, the global hotkey requires a system-wide
keyboard shortcut bound to python -m dictado --toggle (Wayland blocks
keyboard grabs from arbitrary user processes).
Anything matching this shape is accepted:
[<modifier>+ ...] <key>
Modifier tokens (case-insensitive):
| Token | Aliases |
|---|---|
ctrl |
control |
shift |
— |
alt |
— |
win |
cmd, super, meta |
The final token is one of:
- a single character:
a–z,0–9, punctuation - a function key:
f1–f24 - a named key:
space,enter,tab,escape,up,down,left,right,home,end,pageup,pagedown,insert,delete,backspace
Examples that work: alt+t, ctrl+shift+v, ctrl+alt+space, win+h,
f9, `ctrl+``.
If the combo is already owned by another app, the rebind quietly fails,
the previous binding stays, and you'll see a line in daemon.log —
nothing crashes.
| Item | Action |
|---|---|
| Record / Stop | Same as the hotkey. |
| Hotkey ▶ | Pick a preset, or Set custom… for a Tk prompt. |
| Model ▶ | Switch between tiny / base / small / medium / large. |
| Auto-paste after transcription | Toggle the synthesized Ctrl+V step. |
| Quit | Drop the model from RAM and exit. |
The tray icon's colour reflects state: gray (loading), green (ready), red (recording), yellow (transcribing).
Tray menu → Voice activation ("Hey Bijou" / "Hey Biboo" ...).
When checked, Dictado loads a small auxiliary Whisper model
(tiny.en, ~39 MB) onto its own thread and listens to a rolling
2.5-second microphone buffer. Saying any of the configured wake
phrases triggers a recording the same way pressing the hotkey
would.
The default phrase list takes either of two assistant names —
Bijou (bee-joo) or Biboo (bee-boo) — preceded by any
of: hey, ok, okay, yo, hello, hi, greetings,
salutations. So hey bijou, ok biboo, yo bijou, salutations biboo, etc. all work. The matcher is forgiving about how Whisper
transcribes those unusual names: variants like bayou, bee-joo,
bijoux, bibu, and bee boo are all recognised.
To override the phrase list, drop entries into wake_word_phrases
in your config.json:
{
"wake_word_enabled": true,
"wake_word_phrases": [
"hey bijou",
"ok biboo",
"computer activate"
]
}Config locations:
| OS | Path |
|---|---|
| Windows | %LOCALAPPDATA%\dictado\config.json |
| macOS | ~/Library/Application Support/dictado/config.json |
| Linux | ~/.local/share/dictado/config.json |
Each phrase's last word becomes the "name"; recognised names
(bijou, biboo) get phonetic-variant fuzzing applied
automatically. Other names are matched literally. With
wake_word_phrases empty or absent, the default list applies.
The toggle is opt-in. Idle CPU with the listener enabled but nobody talking is ~0.5%; active listening peaks around 5%. Memory cost is ~140 MB for the auxiliary model. Default is OFF, so users who only need the hotkey path pay zero overhead.
The full design — architecture diagram, tuning knobs, troubleshooting,
how to add a new wake name — lives at
docs/WAKE_WORD.md.
Optional startup sound + silence auto-stop. When the listener
triggers a recording (not the hotkey), you can have a short audio
cue play at the moment the mic opens, and the recording will
auto-stop after a configurable period of silence. Both opt-in via
config.json:
{
"wake_sound_path": "C:\\Users\\you\\Documents\\beep.wav",
"wake_sound_volume": 0.7,
"wake_silence_stop_s": 3.0,
"wake_silence_rms_threshold": 0.010
}Empty wake_sound_path = no sound. Set wake_silence_stop_s to
0 to disable the auto-stop. Tuning details and supported file
formats per platform live in docs/WAKE_WORD.md
under "Wake-event extras".
Dictado loads any model the upstream openai-whisper package
accepts — tiny / base / small / medium plus the *.en English-only
variants, plus large-v1, large-v2, large-v3, and large-v3-turbo.
See docs/MODELS.md for the full table and a
picking guide.
The five surfaced in the tray menu by default:
| Tray label | Model name | Disk | CPU RAM | Notes |
|---|---|---|---|---|
| Base | base |
140 MB | 0.5 GB | Best for live partials on a slow CPU |
| Small | small |
460 MB | 1.0 GB | Sweet spot accuracy / speed on a laptop CPU |
| Medium | medium |
1.5 GB | 1.5 GB | High-accuracy multilingual; default on first launch |
| Large v3 Turbo | large-v3-turbo |
1.5 GB | 1.5 GB | ~5× faster than large-v3 on CPU |
| Large v3 | large-v3 |
2.9 GB | 3.0 GB | Highest accuracy; sub-realtime on CPU |
Need an English-only or older-large variant? They are loadable from the CLI:
dictado --switch-model medium.en
dictado --switch-model large-v2benchmark.py runs every model sequentially (only one in RAM at a
time) on a known clip and writes both BENCHMARKS.md and a JSON file.
Reproduce it on your own machine:
python benchmark.py samples/jfk.flac --models tiny base small medium --runs 3Numbers from a 12-core x86 laptop with no GPU, transcribing the ~11-second public-domain JFK clip:
| Model | Median run | Realtime factor | Notes |
|---|---|---|---|
| tiny | 0.66 s | ~17× | Fastest; punctuation imperfect. |
| base | 1.37 s | ~8× | Sweet spot for live partials. |
| small | 3.01 s | ~3.7× | Adds the comma after "Americans". |
| medium | 9.41 s | ~1.2× | Best punctuation; final pass only. |
See BENCHMARKS.md for the full table including load times and the
side-by-side transcriptions that let you grade accuracy by eye.
Settings live at:
| OS | Path |
|---|---|
| Windows | %LOCALAPPDATA%\dictado\config.json |
| macOS | ~/Library/Application Support/dictado/config.json |
| Linux | ~/.local/share/dictado/config.json |
{
"model": "medium",
"hotkey": "alt+t",
"autopaste": true,
"popup": true,
"language": "en",
"archive_dir": null
}archive_dir = null (the default) means "use ~/Documents/Sound Recordings/". On Windows that folder is honoured via
SHGetKnownFolderPath, so OneDrive-redirected Documents folders work
correctly. Set it to a string to force a specific path, or to an empty
string to disable archiving.
Dictado is built so it doesn't trip the kind of behaviour-based detections endpoint protection products use to flag keyloggers and remote-access tools. The full mapping is in docs/SECURITY.md; in summary:
- Hotkey is a Win32
RegisterHotKey(Windows) or pynput global hotkey (macOS / Linux X11). NokeyboardPython lib, noSetWindowsHookEx WH_KEYBOARD_LL, no global low-level hook. - Auto-paste is exactly one synthesised Ctrl+V chord per dictation
via
SendInput/osascript/xdotool. Nokeyboard.write(), no character-by-character keystroke pumping. - IPC between the daemon and CLI shims is a polled trigger file in the per-user state dir. No socket listener, no named pipe.
- Auto-start uses the OS's standard mechanism: a Startup-folder shortcut
on Windows, a
.desktopentry on Linux, a LaunchAgent on macOS. No Scheduled Task.
If your Falcon / Defender for Endpoint / SentinelOne tenant still flags the daemon, docs/SECURITY.md includes a copy-pasteable exception template.
dictado/ runtime package (cross-platform)
├─ daemon.py tray icon, recording loop, popup, transcribe
├─ archive.py WAV writer + rolling weekly transcript log
├─ config.py persisted settings + hotkey grammar parser
└─ platform/
├─ windows.py RegisterHotKey, SendInput, Startup .lnk
├─ macos.py pynput, osascript Cmd+V, LaunchAgent
└─ linux.py pynput / wtype, xdotool, .desktop
scripts/ install helpers (install.ps1, install.sh)
benchmark.py sequential model benchmark
samples/jfk.flac public-domain audio for the benchmark
docs/SECURITY.md endpoint-protection mapping
BENCHMARKS.md measured speed and accuracy per model
ABOUT.md longer-form project background
CHANGELOG.md versioned release notes
MIT. See LICENSE.