Press a hotkey, say your sentence, and it drops straight into whatever you're typing in, or onto your clipboard. Macaw runs faster-whisper on your GPU or CPU, sits quietly in the tray, and works across Wayland (Hyprland, Sway, KDE, GNOME) and X11. Nothing gets uploaded. Nothing phones home.
Table of contents
- 🔒 Local by default. Every local engine runs fully on your machine — no account, no round-trip. The two GPT-4o cloud models are strictly opt-in, with your own key.
- 🆓 Free and open. MIT-licensed, and the models are open too. No subscription, no per-minute meter.
- 🐧 Made for Linux. Wayland or X11, a tray app with one hotkey to fire it. A native win64 build (beta) covers Windows too.
- 🔁 Swap the brain. Start on Whisper, move to Parakeet, Voxtral, Moonshine, or the featherweight sherpa-onnx CPU models whenever you feel like it.
- ⚡ Uses your GPU. CUDA when it's there, CPU when it isn't, and nothing to wire up either way.
Macaw lives in the tray: one window for everything, and a recording overlay you can make your own.
Also in the box:
- One hotkey to start and stop — and it auto-stops when you go quiet.
- Type or clipboard — paste straight into the focused window, or just copy.
- Punctuation hints per language, so commas and periods land where you'd expect them.
- Curated model cards — star ratings, pros/cons, and minimum vs recommended hardware for every model, so you pick right the first time.
- Live typing (alpha) — words appear as you speak, once two passes agree on them.
- Smart paste — picks ydotool, wtype, or xdotool for you, and falls back per window when one misbehaves.
- Sound cues for recording, processing, and done.
- Clean-up formatting — optionally pass the result through a tiny local LLM (or a cloud one) that fixes punctuation, trims filler, and shapes it into an email, message or list. Smart mode detects the kind of text; the prompt is fully editable.
Every model installs on demand from the Model Manager (macaw --models), each in its own sandbox so nothing clobbers your main setup — the app itself stays small. Pick the one that fits your hardware and languages.
| Model | Project | Languages | Download | Runs on | Install |
|---|---|---|---|---|---|
Whisper tiny → large-v3-turbo |
faster-whisper · OpenAI Whisper | 99+ | 75 MB – 3 GB | CPU or GPU | macaw[whisper] |
Moonshine tiny / base |
Useful Sensors Moonshine | English | ~30–60 MB | CPU | macaw[moonshine] |
Moonshine v2 tiny / small / medium |
Moonshine Streaming | English | ~35–250 MB | CPU | macaw[moonshine2] |
Parakeet TDT v2 / v3 |
NVIDIA NeMo | English / 25 | ~2.5 GB | NVIDIA GPU | macaw[nemo] |
| Canary-Qwen 2.5B | NVIDIA NeMo | English | ~5 GB | NVIDIA GPU | macaw[nemo] |
| Voxtral Mini 3B | Mistral Voxtral · Transformers | 13 | ~6 GB | NVIDIA GPU | macaw[voxtral] |
| Zipformer · Paraformer · Parakeet · Nemotron (ONNX) | sherpa-onnx | English · Chinese · 25 | ~26 MB – 660 MB | CPU | macaw[sherpa] |
| GPT-4o Transcribe · mini | OpenAI | 99+ | cloud (API key) | Cloud | macaw[openai] |
The default is large-v3-turbo: 99+ languages, about 1.6 GB, and the best speed-to-accuracy trade-off on a GPU. On a laptop with no GPU, base or distil-small.en keep things snappy.
Turn raw dictation into finished text. Enable it in the LLM tab and Macaw passes the final transcription through a small, fast model that fixes punctuation and capitalization, drops filler ("um", false starts) and formats the result to match what it is — an email, a chat reply, a list. Smart mode detects that on its own; the system prompt is fully editable if you'd rather it always format a certain way. Applies to Clipboard and Type output (live typing streams raw as you speak).
Local models run in their own sandbox via llama.cpp — near-instant on CPU, GPU-accelerated when a CUDA build is present:
| Model | Project | Runs on | Download | Install |
|---|---|---|---|---|
Qwen2.5 0.5B / 1.5B |
Qwen · llama.cpp | CPU or GPU | ~400 MB – 1.1 GB | macaw[llm] |
Llama 3.2 1B |
Meta Llama · llama.cpp | CPU or GPU | ~0.8 GB | macaw[llm] |
Cloud providers — bring your own key in the Cloud Providers window (Settings → Manage providers, or the LLM tab). Built-in presets for OpenAI, Anthropic (Claude), Google Gemini, xAI (Grok), OpenRouter, Groq, Mistral, DeepSeek, Together, a local Ollama server, and any OpenAI-compatible endpoint (set a custom Base URL). Two protocols — OpenAI chat and Anthropic messages — cover them all, over a tiny built-in HTTP client (no SDK needed). API keys are encrypted at rest (secrets.enc + a 0600 key file) — never written to your config.
Offline vs cloud. sherpa-onnx and every other local engine run entirely on your machine; the GPT-4o models are cloud APIs — set an OpenAI key in the Model Manager (or OPENAI_API_KEY) to use them.
Every method gives you the Macaw app (tray + Settings/Models windows + overlay). Set a global shortcut in Settings, or bind macaw --trigger to a key yourself.
| Method | Best for | Command |
|---|---|---|
| Install script | most Linux setups | curl -fsSL https://raw.githubusercontent.com/Osyna/Macaw/main/install.sh | bash |
| AppImage | no install | grab it from Releases, chmod +x, run |
| Tarball | manual installs | macaw-<version>-linux-x86_64.tar.gz from Releases — run ./macaw-ui |
| NSIS installer | Windows 10/11 (beta) | Macaw_<version>_x64-setup.exe from Releases |
First, the system bits Macaw talks to:
# Arch
pacman -S wl-clipboard wtype ydotool portaudio
# Ubuntu / Debian
apt install wl-clipboard wtype ydotool libportaudio2On Wayland, ydotool is the safest pick for type mode: it handles native Wayland apps and XWayland alike. wtype only covers native Wayland windows. On X11, xdotool is enough. You'll also need Python 3.10+.
curl -fsSL https://raw.githubusercontent.com/Osyna/Macaw/main/install.sh | bashIt downloads the latest AppImage to ~/.local/bin, adds the desktop launcher and icon, and offers to set up input-group access for the evdev hotkey and ydotool. Run install.sh uninstall to remove it again.
Each bundle ships the native UI and the speech engine (Python is embedded — no system Python needed), and weighs a fraction of what a models-included build would. Every backend — Whisper included — installs on demand from the Model Manager into its own sandbox under ~/.local/share/macaw/backends/ (one click; GPU wheels are added automatically on NVIDIA boxes, and a private copy of uv is fetched on the first install if you don't have it). The overlay anchors via wlr-layer-shell — Hyprland/Sway/KDE support it out of the box.
Run Macaw_<version>_x64-setup.exe from the latest release — the same native UI as Linux (tray, Models, Appearance, recording overlay), or grab macaw-<version>-win64.zip for a portable install (run macaw-ui.exe).
What's different on Windows:
- Hotkey uses the native
RegisterHotKeyAPI — set it in Settings as usual (noinputgroup, no evdev). - Typing uses
SendInput— no ydotool/wtype/xdotool needed. - Models: Whisper, sherpa-onnx, Moonshine, Voxtral, and the GPT-4o cloud models work; NeMo (Parakeet GPU / Canary-Qwen) is Linux-only. Sandboxed installs from the Model Manager fetch a private
uvon first use. - Autostart: flip Settings → Start at login (a
HKCU\…\Runentry). - Config lives at
%USERPROFILE%\.config\macaw\config.yaml.
uv tool install "macaw @ git+https://github.com/Osyna/Macaw"
macaw --download large-v3-turbo # fetch a model
macaw --run # headless engine (hotkey + overlay-less dictation)That gives you the engine and the macaw CLI without the desktop app — dictation still lands in your clipboard or focused window. Backends install from macaw --setup, or as extras: macaw[sherpa], macaw[moonshine], macaw[moonshine2], macaw[nemo], macaw[voxtral], macaw[openai], macaw[cuda].
| Command | What it does |
|---|---|
Macaw (app) |
Tray + Settings/Models + recording overlay. Spawns and owns the engine. |
macaw --run |
The engine, headless — for CLI-only installs. |
macaw --trigger |
Toggles recording. Bind this to a key. (AppImage installs get a thin macaw wrapper; args are handled by the running app.) |
macaw --settings / --models / --stop |
Open a tab / quit the running app. |
macaw-cli |
Standalone push-to-talk in the terminal. No engine needed. |
Built-in (recommended). Open Settings → Global shortcut, turn it on, click the field and press your combo — including Super/Meta combos, which Macaw captures straight from evdev so a Wayland compositor (Hyprland, etc.) can't swallow them. It's off by default, and the same shortcut works on X11 and every Wayland compositor with no per-desktop config. It needs read access to input devices; the installer already adds you to the input group for ydotool (log out and back in once after the first install). It only monitors input, so your combo still reaches other apps.
Or bind it yourself. Prefer your compositor's own keybinds? Point one at macaw --trigger:
# Hyprland (hyprland.conf)
bind = , F9, exec, macaw --trigger
# Sway (config)
bindsym F9 exec macaw --triggerOn KDE or GNOME, add a custom keyboard shortcut that runs macaw --trigger.
Settings live behind the tray icon. They're saved to ~/.config/macaw/config.yaml (or $XDG_CONFIG_HOME/macaw/config.yaml), and you can edit the file by hand:
device_index: null # microphone index (null = system default)
language: en # Whisper language code
model: large-v3-turbo # see the Model Manager for the full list
output_mode: clipboard # "clipboard" or "type"
silence_timeout: 3.0 # seconds of silence before it auto-stops
window_position: bottom_center
sound_enabled: true
punctuation_hints: true # nudge Whisper toward natural punctuation
streaming: false # live typing as you speak (alpha)Output modes. In clipboard mode the text is copied and the overlay shows a checkmark. In type mode the overlay hides first, so it won't steal focus, then the text is pasted into whatever window was focused when you started.
Punctuation hints. When on, Macaw feeds Whisper a well-punctuated sentence in your language as the initial_prompt, which biases it toward commas, periods, and question marks. Works for English, French, German, Spanish, Italian, Portuguese, Dutch, Polish, Russian, Japanese, and Chinese.
Live typing (alpha). With type mode on, Macaw re-transcribes about once a second while you talk and types the words that two consecutive passes agree on. A final pass flushes the rest when you stop. It runs inference on a growing audio window, so it costs more GPU. Off by default.
Macaw (native Slint app: tray, Settings/Models, overlay)
| \
| spawns \ WebSocket (JSON-RPC + events, 127.0.0.1, token-authed)
v \
macaw-engine <----+ macaw --trigger --[ZMQ IPC]--> macaw-engine
|
+-- AudioCapture (sounddevice)
+-- Transcriber (facade) --> macaw.stt backends
+-- DesktopHelper (clipboard, paste, focus)
+-- HotkeyListener (evdev / RegisterHotKey)
- The UI is a native Slint app (pure Rust, no webview); the engine is headless Python. The app spawns the engine and holds its stdin — if either dies, the other follows.
- CLI IPC is ZMQ REQ/REP over a Unix socket at
$XDG_RUNTIME_DIR/macaw.ipc(TCP on Windows). - The UI drives the engine over a local WebSocket: config, model management, recording state, live mic levels for the overlay bars.
- Audio is captured at 16 kHz mono with energy-based speech detection.
- Transcription runs through pluggable backends; the default is
large-v3-turboon faster-whisper, behind an adaptive energy silence gate. - Pasting uses ydotool (evdev), wtype (Wayland virtual keyboard), or xdotool (X11), with XWayland detection on Hyprland.
Transcriber is a thin facade. It normalizes audio to mono float32 at 16 kHz and gates silence, then hands off to whichever backend the configured model points at. Every backend — Whisper included — carries a native dependency stack of its own, so each lives in its own isolated venv under ~/.local/share/macaw/backends/<extra>/, driven by a sidecar worker (stt/worker.py) that swaps audio and text over a pipe. The Model Manager's Install button builds that venv for you, and nothing touches the main environment.
macaw-slint/ is the frontend on both platforms: pure Rust with
Slint — one ~13 MB binary, no webview, no GTK, no async
runtime. Same engine, same WebSocket protocol. The tray is StatusNotifierItem
on Linux and a Win32 notification icon on Windows. On Wayland the overlay is
a dedicated layer-shell client (macaw-overlay); on Hyprland it's positioned
through runtime window rules; everywhere else (GNOME, Windows) it's a
frameless always-on-top window the app places itself.
cd macaw-slint && cargo build --release
MACAW_ENGINE_CMD="uv run macaw-engine" ./target/release/macaw-ui # dev engine
# packaged: put a `macaw-engine` binary next to `macaw-ui`, or have it on $PATHAdding a model is two small files: a backend and a YAML entry.
# src/macaw/stt/mybackend.py — the code (metadata lives in YAML, not here)
from macaw.stt.base import Backend
from macaw.stt.registry import register
@register
class MyBackend(Backend):
key = "mybackend" # models bind to this key
def load(self, model_path=None): ... # load into memory
def transcribe(self, audio, sample_rate=16_000) -> str: ... # mono f32 16kHz# src/macaw/stt/models/mybackend.yaml — the catalog metadata
backend: mybackend # matches Backend.key above; inherited by each model
models:
- id: my-model
label: My Model
size: "~1 GB"
speed: fast
languages: EN
hardware: "CPU / Any"
vram: "—"Import the module in src/macaw/stt/__init__.py so the class registers; the YAML is read at import. The model then shows up in the Model Manager with its hardware, VRAM, and size. If the backend's dependencies clash with the main environment, subclass SubprocessBackend instead (set key only) and add a loader to stt/worker.py; it installs into an isolated venv and runs out-of-process. See tests/test_stt.py for the contract.
Engine logs are the app's stderr, prefixed [engine] — run Macaw (or the AppImage) from a terminal to watch them live.
Type mode does nothing. You need at least one paste tool (ydotool, wtype, or xdotool); watch the logs for "No paste tool available". For ydotool, the ydotoold daemon has to be running and your user needs access to /dev/uinput, usually via the input group.
Text lands in the wrong window. Macaw grabs the active window before it shows the overlay. Switch windows mid-recording and the text follows the original one. That's on purpose.
No GPU acceleration. Check CUDA with python -c "import ctranslate2; print(ctranslate2.get_cuda_device_count())". A 0 means CUDA libraries aren't on your LD_LIBRARY_PATH, or you need the [cuda] extra.
Second instance won't start. Only one engine can hold the IPC socket; launching the app again just focuses the running instance.
Electron apps miss the paste. On Wayland, wtype sends virtual keyboard events some Electron apps read wrong. Macaw uses Shift+Insert instead of Ctrl+V for wtype, and prefers ydotool, which works everywhere.
Releases are built by CI whenever a vX.Y.Z tag is pushed: release.yml ships the wheel/sdist and the Linux bundles (AppImage + tarball with the native Slint UI and the embedded engine) plus SHA256SUMS; windows.yml adds the NSIS installer.
Cut one in a single step. make tag bumps the version in pyproject.toml and macaw-slint/Cargo.toml, commits, and tags:
make tag VERSION=0.2.0
git push && git push origin v0.2.0To refresh the AUR package after the release exists, run packaging/aur/bump.sh 0.2.0 from an Arch checkout (it fetches the tag tarball, writes the sha256 and .SRCINFO), then commit and push those to the macaw AUR repo.
Bug reports, ideas, and pull requests are all welcome. Open an issue to start. Run ruff check and python tests/test_stt.py before you push, and keep changes focused.
MIT. Use it, fork it, ship it.
Macaw stands on a lot of good open-source work:
- faster-whisper and OpenAI Whisper for the default engine
- NVIDIA NeMo for Parakeet and Canary-Qwen
- Mistral Voxtral and Hugging Face Transformers
- Useful Sensors Moonshine for the featherweight option
- sherpa-onnx (k2-fsa) for streaming ASR on plain CPUs
- Slint, sounddevice, and uv
If Macaw saves you some typing, a ⭐ helps other people find it.





