mynah · the bird that repeats what it hears

Say it. It types where you are.

Press a key and talk. Your words land in whatever window has focus — a commit message, a chat, a form — as you pause, not when you stop. The speech never leaves the machine you are sitting at.

Read the source Install
  • no account · no API key
  • macOS and Linux · Wayland
  • MIT
the bird that says back what it hears

What it is like

Three states, and only one of them is on screen

There is no window to arrange and no app to switch to. A pill shows what Mynah is doing, the menu bar or tray item carries start, stop and quit, and the rest of the time you are simply typing — with your voice.

idle

the hotkey is armed, nothing is listening

listening

your voice in twelve bands — the only thing on screen

transcribing

you paused; that utterance is going into the app

The model stays warm for 45 seconds after you finish, so back-to-back dictation is instant. Then it unloads, and Mynah costs nothing until the next time you press the key.

The difference

Text lands in the gaps, not at the end

Most dictation tools wait until you stop talking, hand the whole recording to a model and paste the result. Mynah splits what you say into utterances and types each one the moment you pause — so a long thought arrives in pieces you can watch, and a short one arrives immediately.

one session · four utterancestyped as they close
the fee is computedon gross,should be net— write that down

Why it matters more than it sounds

You can see the words while you are still speaking, so a misheard phrase is caught in the sentence you are in, not three paragraphs later. And a pause is a natural place to stop: nothing is lost if you change your mind.

When you want the other thing

mynah set vad=off turns segmentation off: the whole session is transcribed as one block when it ends. Useful for a dictated paragraph you want punctuated as a whole — and slower to appear, by design.

The part nobody sees

Tuned against recordings, not guessed at

Deciding when an utterance ended is the whole game. Too eager and a sentence is cut in half; too patient and the text arrives after you have already moved on. Those thresholds live in tuning/tuning.toml — how long a silence closes an utterance, how much trailing silence to trim, how far above the room speech must sit.

They are pinned by a golden corpus: recordings with known boundaries, in tuning/golden/. Change a threshold and the test suite says which recording moved.

The noise floor is measured at the start of a session, but its contribution is capped at the speech line — a calibrated gate above it would demand speech louder than speech, and no setting could counter that. Frames captured during calibration are kept, not dropped: start talking immediately and that speech is still segmented.

tuning/tuning.toml

utterance_silence         = 0.8    # silence that closes one
trailing_padding          = 0.2    # kept, so endings survive
noise_calibration_seconds = 1.0    # measured at session start
calibration_speech_floor  = 0.03   # and capped here

tuning/golden/ — what holds it to that
  quiet_two_utterances.wav          
  speech_during_calibration.wav     
  speech_over_noise_in_calibration  
  gap_below_silence.wav             
  trailing_silence_trim.wav         
  noisy_room.wav                    

Too sensitive in a café, not sensitive enough in a quiet room: mynah set frame_energy=0.02 raises your own floor. The contract is untouched — calibration can lift the gate above it, never below.

Local by definition

There is no endpoint to turn off

Speech is transcribed on your own hardware — mlx-whisper on the Apple GPU, whisper.cpp elsewhere. There is no account, no API key and no telemetry, because there is nothing to send anything to. The only thing Mynah writes outside its own settings file is the text it types, into the window you were already in.

A dictation tool hears everything you say at your desk. That is the one kind of software where "it runs locally" should not be a setting you have to find.

Where it runs

One engine, a set of providers per platform

Speech, typing and the indicator are interfaces; a platform is a set of implementations. That is why the Linux port is a handful of providers rather than a rewrite — and why the tuning contract is shared rather than re-derived.

 macOSLinux · Wayland
The engine, segmentation and tuningone implementation, shared — a platform never re-derives it
Speech, on your own hardwaremlx-whisper, Apple GPUwhisper.cpp
Typing into the focused windowAccessibility APIwtype, on wlroots and KDE
The hotkeypynputyour compositor's, bound to mynah toggle
Indicator and menunative pill and menu barthe shell's, through mynah watch
Runs at loginSMAppServicesystemd --user
Where the face comes fromMynah.app, no Python at runtimethe desktop’s own shell

Wayland only on Linux, deliberately: on X11 any client can already read the keyboard and inject keystrokes, so there is nothing to grant and nothing to revoke. The gap is GNOME, which does not implement the virtual-keyboard protocol wtype needs; a portal injector for it is not written yet.

Install

One command, then a guided first run

macOS
$ pipx install "git+https://github.com/ReidenXerx/mynah.git"
$ pipx inject mynah "mynah[macos] @ git+https://github.com/ReidenXerx/mynah.git"

Linux
$ pipx inject mynah "mynah[linux] @ git+https://github.com/ReidenXerx/mynah.git"
$ sudo pacman -S whisper-cpp wtype wl-clipboard

$ mynah setup

mynah — first-time setup

   Runtime extra — sounddevice, pynput, webrtcvad, pyobjc
   Accessibility — Not granted yet
      System Settings → Privacy & Security → Accessibility
      Add mynah and enable it, then re-run: mynah setup
   Microphone — Reachable
   Hotkey — Valid (<cmd>+<shift>+.)

The extras are requested from this repository, never as a bare mynah[macos] — that name on PyPI is an unrelated package. The first run installs the speech runtime itself, then names whatever is still missing and what to do about it. The login service is installed last, once the rest passes — never on top of a check that failed.

The settings worth knowing

hotkeyThe global key, pynput syntax — <f8>, <ctrl>+<space>
triggertoggle to press twice, ptt to hold while you talk
languageThe language you speak
vadOff means the text arrives only when the session ends
frame_energyYour floor for what counts as speech — lower hears more of the room
auto_stop_silenceSeconds of silence that end a session by themselves
idle_timeoutHow long the model stays warm afterwards (0 = never unload)

mynah config prints all of them with the file they live in; mynah set key=value changes one.