Skip to main content

Local voice dictation for macOS. Whisper + Llama 3.2 3B run on-device via Apple's MLX framework. No accounts, no API keys, no network.

Project description

Susurro

Local voice dictation for macOS. Fully offline. MIT licensed.

Hold a hotkey, talk, release. The transcript is polished into structured text — ordinals become numbered lists, fillers get stripped, self-corrections get applied — and pasted at the cursor in any app. Everything runs on your Mac through Apple's MLX framework. No accounts, no API keys, no network.

License: MIT macOS Apple Silicon Python Version PyPI Landing

Install (one line)

curl -fsSL https://raw.githubusercontent.com/danilobrando/susurro/main/install.sh | bash

Or with pipx:

pipx install susurro
susurro

After install, hold the right Option key (⌥) to dictate. The first launch downloads Whisper + Llama weights (~5 GB total, one-time) and triggers three macOS permission prompts (Microphone, Accessibility, Input Monitoring).

Why Susurro

  • Fully offline. Audio never leaves your machine. No telemetry, no analytics, no cloud calls during normal use.
  • WisprFlow-grade smart formatting. An LLM polishes the raw transcript: ordinals become numbered lists, fillers like "um/eh/o sea" get removed, self-corrections ("Pedro, eh, Pablo digo") collapse to the final intent.
  • Auditable. Every (raw → polished) edit is logged locally to ~/.susurro/polish.jsonl. Nothing is hidden.
  • MIT licensed. Read the code, fork it, redistribute it.

If you want zero local RAM + lower latency at the cost of cloud transcription, Susurro Pro is a paid hosted variant that extends this package.

Requirements

  • Apple Silicon Mac (M1 or later). MLX doesn't support Intel.
  • macOS 13+ recommended (tested on 26).
  • Python 3.10+.
  • ~5 GB free disk (one-time model download).
  • ~3 GB free RAM while running.

Usage

  1. Click into any text field.
  2. Hold the right Option key (⌥) and speak.
  3. Release. After ~1.5 s, the polished transcript pastes at the cursor via Cmd+V.

While recording, a small dark waveform pill appears near the bottom of the active screen, with 16 white bars rippling to your voice. Toggle off via the Show waveform indicator menu item.

Menu bar icon reflects state:

Icon Meaning
🎙 idle Ready. Hold the hotkey to record.
🔴 recording Listening. Release to transcribe.
⏳ processing Transcribing + polishing on-device.

Smart formatting

The polish step turns raw dictation into structured text. Three modes (switchable from the menu):

  • Off — paste raw STT output unchanged.
  • Rules only — regex cleanup: filler removal (eh, mmm, o sea sí, um, uh), whitespace normalization. <5 ms.
  • Smart (LLM) — rules + local Llama 3.2 3B polish, but only when triggers fire (ordinal markers, backtrack phrases, long-form input). Otherwise stays rules-only to keep latency low.

Example (smart mode):

Raw:   "Vamos a seguir tres pasos. Primero, reinicia. Segundo, vuelve a registrarte. Tercero, envía un correo."

Polished:
Vamos a seguir tres pasos.

1. Reinicia
2. Vuelve a registrarte
3. Envía un correo

Configuration

Edit susurro/config.py:

  • STT_BACKENDlocal (default). Extension packages can register more.
  • POLISH_MODEsmart (default), rules, or off.
  • LOCAL_STT_MODELwhisper-large-v3-turbo (default), or whisper-large-v3-mlx for max accuracy.
  • LOCAL_POLISH_MODELLlama-3.2-3B-Instruct-4bit (default), or any mlx-community 3B-class model.
  • HOTKEYalt_r (default). Any pynput Key name: alt_l, ctrl_r, f19, etc.
  • LANGUAGENone for auto-detect, or pin to "es" / "en" to save ~100 ms per request.
  • INPUT_DEVICE — pick a specific mic. Run python -m sounddevice to list devices.

Permissions

macOS will prompt for three permissions the first time you run Susurro:

  1. Microphone — to capture your voice.
  2. Accessibility — to paste the transcript into the focused app.
  3. Input Monitoring — to listen for the global hotkey.

After granting any of these, fully quit and relaunch your terminal for the new permission to take effect. The menu bar has direct links to each pane.

Architecture

audio (sounddevice → 16kHz mono float32)
    → MLX Whisper (whisper-large-v3-turbo, on-device)
    → raw text
    → Polisher
        ├ Tier 1: regex rules (filler removal, whitespace)
        ├ Tier 2: trigger check (ordinals / backtrack / long-form)
        └ Tier 3: MLX-LM polish (Llama 3.2 3B Instruct, on-device)
    → polished text
    → clipboard write + Cmd+V into focused app

Source layout — under ~1500 lines of Python total:

susurro/
  config.py            # all tunables
  audio.py             # mic capture + peak_level for indicator
  hotkey.py            # pynput global hotkey
  typer.py             # clipboard / keystroke insertion
  indicator.py         # floating waveform pill (PyObjC)
  permissions.py       # System Settings deep links
  app.py               # rumps menu bar + main loop (subclassable)
  backends/
    base.py            # protocols (Transcriber, PolishLLM)
    local_mlx.py       # local Whisper via MLX
    local_mlx_lm.py    # local polish LLM via mlx-lm
    __init__.py        # factories + extension registration
  polish/
    __init__.py        # Polisher orchestrator
    rules.py           # regex cleanup
    triggers.py        # decides if LLM should fire
    prompt.py          # system prompt + few-shot examples
  icons/               # template PNGs for menu bar

Extending Susurro

External packages can register additional backends without modifying this code:

# in your_extension/__init__.py
from susurro.backends import register_transcriber, register_polish_llm

class MyCloudSTT:
    name = "mycloud"
    def warmup(self): ...
    def transcribe(self, audio): ...

register_transcriber("mycloud", lambda: MyCloudSTT())

Then set STT_BACKEND = "mycloud" in susurro/config.py or via environment.

Troubleshooting

  • Menu bar icon invisible — emoji-only menu bar items can be hidden on MacBooks with a notch. This release ships a real template PNG, which fixes it for most users.
  • "This process is not trusted" — Accessibility permission isn't granted. Use the Open Accessibility Settings… menu item, then fully restart the terminal.
  • Hotkey doesn't trigger — Input Monitoring permission is missing.
  • Silent recordings / empty transcript — Microphone permission is missing, or INPUT_DEVICE is pointing at the wrong device.
  • First transcription is slow — the model is still warming up. Wait until the menu shows Status: idle before the first real dictation.

Logs land in ~/.susurro/susurro.log; polish events in ~/.susurro/polish.jsonl.

Contributing

See CONTRIBUTING.md. PRs welcome; please keep the package under ~1500 lines.

Security

See SECURITY.md. Report vulnerabilities privately to the maintainer.

Maintainer

Built and maintained by Danny Bravo (dannybravo@gmail.com). Product strategist, AI ecosystem builder, educator — based in Bogotá.

License

MIT © 2026 Danny Bravo.

Credits

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

susurro-0.4.0.tar.gz (32.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

susurro-0.4.0-py3-none-any.whl (30.3 kB view details)

Uploaded Python 3

File details

Details for the file susurro-0.4.0.tar.gz.

File metadata

  • Download URL: susurro-0.4.0.tar.gz
  • Upload date:
  • Size: 32.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.9

File hashes

Hashes for susurro-0.4.0.tar.gz
Algorithm Hash digest
SHA256 4d0f31cd34c6a554c5604a2409ae835c9d89eb6a7cc30ad30402c2e1e71a8e79
MD5 08eda6ac0cf5f17fbf20d001d80e5629
BLAKE2b-256 ec66d53cb817b76f0c737cb7623694fd37c8537604b4bc3a494a7f577ce9d51b

See more details on using hashes here.

File details

Details for the file susurro-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: susurro-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 30.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.9

File hashes

Hashes for susurro-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cec424e78ec8a17dffb3af4ecd982123853ecd55d04905f92ae2d16585d281b4
MD5 2186c5baafaadc920aa26c664f9dfe91
BLAKE2b-256 5ed0e241aff294ee8d6a1abd680c5970daca3e0e9f9925a8080c422798c9de20

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page