Skip to content

benmaster82/writher

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

89 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Writher

WritHer

Offline voice dictation & voice assistant for Windows β€” paste text anywhere, manage notes & reminders hands-free, and speak symbols & code directly (say forward slash, dash, one two three β†’ / - 123).

🌐 getwrither.com Β· ⬇️ Download Β· 🎬 Demo video

Windows Python 3.11+ Faster Whisper Ollama MIT License

Writher Demo Video


πŸ†• What's New

  • πŸš€ Visible first-launch feedback β€” the widget now appears right away with a "Downloading speech model…" status and animated loading eyes while the model loads, then confirms "Ready β€” hold β€Ήyour hotkeyβ€Ί". No more silent minutes where the app looked dead.
  • πŸ‘‹ Welcome toast β€” the very first launch ends with a Windows notification explaining both hotkeys. If the model download fails (no internet on first launch), a clear error state and toast tell you what to do.
  • πŸ” Second-instance toast β€” launching WritHer twice now shows "WritHer is already running" instead of exiting silently.
  • πŸ“š Custom vocabulary β€” teach WritHer to render your spoken jargon or acronyms as their written form. Case-insensitive whole-word matching, multi-word spoken forms supported. Priming terms also feed faster-whisper's initial_prompt to nudge recognition.
  • πŸ”’ Symbol & spelling mode (opt-in) β€” enable in Settings and say "forward slash", "dash", "semicolon", or number words to get actual characters. Spell code letter-by-letter: "W H forward slash F A T" β†’ WH/FAT. Contractions (don't, we're) stay intact and prose is never mangled.
  • 🌐 Recognition language dropdown β€” pick Whisper's language independently of the UI: Auto (default), en, it, de. The detected language is logged for every clip.
  • πŸ“‹ Clipboard restore (default) β€” your clipboard is saved before paste and restored after. Optional toggle keeps the transcript in the clipboard for re-pasting.
  • πŸ”’ Single-instance lock (per-session) β€” launching a second copy exits immediately. Now scoped to the current Windows user so it does not block other users on the same machine.
  • 🎨 Per-mode colour themes β€” dictation widget renders in cyan, assistant in violet.
  • ⌨️ Combo hotkeys β€” assistant hotkey is now Ctrl+Alt+R (avoids browser conflicts). Settings window captures live key combos.
  • πŸ“œ Log viewer in Settings β€” tail the latest log lines directly from the Settings window.
  • πŸ—‘οΈ Delete by voice - say "delete the dentist appointment" or "remove the shopping list" and WritHer finds and removes it. Voice confirmation required before any deletion (15s timeout).
  • ⌨️ Customizable hotkeys - change dictation and assistant shortcuts from Settings. Press the ⌨ button, hit any key, done. No restart needed.
  • πŸŽ™οΈ Microphone selection - pick your input device from Settings, with hot-plug refresh
  • πŸ”„ Toggle recording mode - press once to start, press again to stop (alternative to hold)
  • ⏱️ Safety timeout - auto-stops recording in toggle mode if you forget
  • 🎨 Redesigned UI - CustomTkinter with unified Pandora Blackboard theme (pure black + bright white)
  • πŸ–₯️ Resizable Notes window - drag to resize, maximize/restore, DPI-aware
  • ⚑ Faster Ollama responses - timeout increased from 10s to 30s for larger models
  • πŸ› Bug fixes - clean shutdown, widget positioning, visual artifacts removed
  • πŸ”Œ Reliable mic switching - select any microphone (USB, Bluetooth, AirPods) from Settings and switch on the fly without restart. Tested with Bluetooth HFP devices.
  • πŸ“¦ Standalone exe - download and run, no Python installation required. Whisper model downloads automatically on first launch.
  • βš™οΈ Full Settings panel - Ollama model/URL, Whisper model, language, microphone, recording mode - all configurable from the tray menu.
  • 🎯 Toggle mode fix - resolved issue where Ctrl+Alt+R required double-press to start recording. Added debounce to prevent key-repeat interference.
  • 🎀 Sample rate fix - microphones with 48kHz default (e.g. Logitech C310) now work correctly. Audio is recorded at 16kHz when possible, resampled if not.
  • πŸ“ Portable data paths - database, logs, and recovery files stored in %APPDATA%/WritHer when running as exe, preventing permission issues.
  • βœ“ Cleaner widget feedback - assistant confirmations show minimal icons instead of long text that overflowed the widget.

πŸ’¬ Feedback welcome! If you test WritHer with different microphones or setups, please open an issue and let us know how it goes. Your feedback helps improve the app for everyone.


What is WritHer?

WritHer sits quietly in your system tray and gives you two super-powers:

Mode Hotkey (default) What it does
Dictation AltGr Transcribes your voice and pastes the text directly into whichever app has focus - editors, browsers, chat windows, anything. Optional Symbol & spelling mode substitutes spoken symbols and digits.
Assistant Ctrl+Alt+R Understands natural-language commands and saves notes, creates appointments, sets reminders, manages lists - all by voice.

Both hotkeys are fully customizable from the Settings window, click the ⌨ button next to each shortcut and press your preferred key. The change takes effect immediately, no restart required.

Both hotkeys support two recording modes, configurable from the Settings window in the system tray:

Recording mode How it works
Hold (default) Hold the key to record, release to stop.
Toggle Press once to start recording, press again to stop. A configurable safety timeout auto-stops the recording if you forget.

Everything runs locally: speech recognition via faster-whisper, intent parsing via Ollama, and data stored in a local SQLite database. No cloud, no API keys, no telemetry.


Features

  • Real-time dictation - speak and text appears. Supports both hold-to-record and toggle (press to start/stop) modes. Clipboard is saved and restored automatically (opt-in toggle to keep the transcript in the clipboard).
  • Custom vocabulary - a user-defined map of spoken form β†’ written form, applied whole-word and case-insensitively before any symbol substitution. Multi-word spoken forms and longest-first precedence are supported. Editable from Settings and persisted in the SQLite DB.
  • Priming terms - a free-text list joined into faster-whisper's initial_prompt to bias recognition toward domain terms. Labelled best-effort in the UI hint.
  • Symbol & spelling mode (opt-in) - toggle in Settings. When ON, spoken symbol names and number words are substituted ("W H forward slash F A T" β†’ WH/FAT, "one two three" β†’ 123, "semicolon" β†’ ;) and letter-by-letter spelling is glued. Contractions like don't are preserved and prose is never mangled: multi-character words on either side of a symbol block gluing.
  • Recognition language - independent of the UI language. Set to Auto (default) to let Whisper detect each clip, or pin to en / it / de.
  • Voice-controlled assistant - save notes, create shopping/todo lists, schedule appointments, set reminders, and delete items by voice. All through natural speech.
  • Voice delete with confirmation - say "delete the shopping list" or "remove the dentist appointment". WritHer finds the item by keyword and asks for voice confirmation before deleting. 15-second timeout for safety.
  • Smart date parsing - say "remind me tomorrow at 9" or "meeting next Monday at 3pm" and the LLM converts relative times to absolute datetimes.
  • Toast notifications - get Windows notifications when reminders fire or appointments are approaching.
  • Animated floating widget - a minimal pill-shaped overlay with expressive "Pandora Blackboard" eyes that react to state (listening, thinking, happy, error, etc.). Cyan for dictation, violet for assistant.
  • Notes & Agenda window - a dark-themed resizable window to browse, check off list items, and delete notes/appointments/reminders. Supports maximize/restore and drag-to-resize.
  • Settings window - configure recording mode, max recording duration, keyboard shortcuts, and microphone device directly from the system tray. All settings are persisted across restarts.
  • Customizable combo hotkeys - reassign dictation and assistant keys (including multi-key combos like Ctrl+Alt+R) from the Settings window. Blocked keys are rejected, duplicate detection prevents conflicts.
  • Microphone selection - choose your input device from a dropdown in Settings. Supports hot-plug detection with a refresh button - no restart needed.
  • Single-instance protection - a per-session mutex prevents two copies running at once, eliminating the double-paste bug.
  • Modern UI - built with CustomTkinter and a unified "Pandora Blackboard" theme (pure black + bright white) defined in a single theme.py file.
  • Multi-language UI - ships with English, Italian and German; easy to add more via the locales.py string table.
  • Log viewer - the Settings window tails the latest lines of writher.log for quick diagnostics.
  • Fully offline - no internet required after model download.

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     main.py                          β”‚
β”‚            (orchestrator + Tk event loop)             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ hotkey   β”‚ recorder  β”‚ widget    β”‚ tray_icon          β”‚
β”‚ listener β”‚ (audio)   β”‚ (overlay) β”‚ (system tray)      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                      β”‚
β”‚  Dictation pipeline          Assistant pipeline      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”           β”‚
β”‚  β”‚transcriberβ”‚               β”‚transcriberβ”‚           β”‚
β”‚  β”‚ (Whisper) β”‚               β”‚ (Whisper) β”‚           β”‚
β”‚  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜               β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜           β”‚
β”‚        β–Ό                           β–Ό                 β”‚
β”‚  replacements.py             assistant              β”‚
β”‚  (user vocab +               (Ollama LLM             β”‚
β”‚   opt-in symbols)            + function calls)       β”‚
β”‚        β”‚                           β”‚                 β”‚
β”‚        β–Ό                           β–Ό                 β”‚
β”‚   injector                    database               β”‚
β”‚  (clipboard                  (SQLite)                β”‚
β”‚   + Ctrl+V)                        β”‚                 β”‚
β”‚                              notifier                β”‚
β”‚                          (toast scheduler)            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Requirements

  • Windows 10/11
  • A working microphone
  • Internet connection on first launch (to download the Whisper speech model)
  • Ollama installed and running locally (only required for assistant mode; dictation works without it)

Ollama setup: download and install from ollama.com, then pull the configured model, for example:

ollama pull llama3.1:8b

Ollama runs as a background service on Windows. If the assistant hotkey is triggered while Ollama is not reachable, WritHer shows a toast notification and aborts the request β€” dictation is unaffected.


Installation

Option A: Download the exe (recommended)

  1. Download WritHer-v1.3.1-win64.zip from the latest release
  2. Extract to any folder
  3. Install and start Ollama (only needed for assistant mode)
  4. Run WritHer.exe
  5. On first launch, the Whisper model is downloaded automatically
  6. Right-click the tray icon for Settings and Notes & Agenda

Option B: Run from source

1. Clone the repository

git clone https://github.com/benmaster82/writher.git
cd writher

2. Create a virtual environment (recommended)

python -m venv venv
venv\Scripts\activate

Requires Python 3.11+

3. Install dependencies

pip install faster-whisper numpy sounddevice pynput pystray Pillow requests winotify customtkinter

Optional: install plyer as a fallback notification backend:

pip install plyer

4. Run

python main.py

Writher appears in the system tray. Hold AltGr to dictate, hold Ctrl+Alt+R for assistant commands. Make sure Ollama is running (ollama serve or the installed service) before invoking the assistant.


Configuration

All settings live in config.py:

# Hotkeys (defaults β€” can be changed from Settings at runtime)
HOTKEY = Key.alt_gr                                          # Dictation
ASSISTANT_HOTKEY = (frozenset({"ctrl", "alt"}), KeyCode.from_vk(82))  # Ctrl+Alt+R

# UI language ("en", "it" or "de") β€” controls interface strings only.
LANGUAGE = "en"

# Recognition language (Whisper). None = per-clip auto-detect.
WHISPER_LANGUAGE = None

# Clipboard behaviour after paste. False = restore previous clipboard.
KEEP_TRANSCRIPT_IN_CLIPBOARD = False

# Recording mode
HOLD_TO_RECORD = True          # True = hold key, False = toggle (press/press)
MAX_RECORD_SECONDS = 120       # Safety timeout for toggle mode (seconds)

# Microphone
MIC_DEVICE_NAME = None         # None = system default, or device name (str)

# Whisper
MODEL_SIZE = "small"           # tiny, base, small (default), medium, large-v3
DEVICE = "cpu"                 # "cpu" or "cuda"
COMPUTE_TYPE = "int8"          # int8, float16, float32

# Ollama
OLLAMA_URL = "http://localhost:11434"
OLLAMA_MODEL = "llama3.1:8b"

# Notification lead time
APPOINTMENT_REMIND_MINUTES = 15

Note: HOLD_TO_RECORD, MAX_RECORD_SECONDS, MIC_DEVICE_NAME, HOTKEY, and ASSISTANT_HOTKEY can also be changed at runtime from the Settings window in the system tray. Changes made there are persisted in the database and override config.py defaults.

Choosing a Whisper model

Model Size Speed Accuracy
tiny 39 MB ⚑ fastest basic
base 74 MB ⚑ fast good
small 244 MB moderate better (default)
medium 769 MB slower great
large-v3 1.5 GB slowest best

The small model is the default because it handles symbol and code spelling (e.g. "forward slash") reliably. The base model can mishear multi-word phrases and is not recommended for code dictation.

For CUDA acceleration, install ctranslate2 with CUDA support and set DEVICE = "cuda".


Usage

Dictation mode

Hold mode (default):

  1. Focus any text field (editor, browser, chat…)
  2. Hold AltGr
  3. Speak
  4. Release - transcribed text is pasted automatically

Toggle mode:

  1. Focus any text field
  2. Press AltGr once to start recording
  3. Speak
  4. Press AltGr again to stop - transcribed text is pasted automatically

In toggle mode, a safety timeout (configurable in Settings) will auto-stop the recording if you forget to press the key again.

Symbol & spelling mode

Off by default. Enable the toggle in Settings to substitute spoken symbol names and number words, and to glue letter-by-letter spelling. Prose stays intact β€” the spacing compaction only fires when both neighbours of a symbol are single characters or digit sequences, so "The 100 meter dash was thrilling" becomes "The 100 meter - was thrilling" rather than gluing across words. Contractions like don't and we're are always preserved.

You say You get
forward slash /
back slash \
dash / hyphen / minus -
semicolon ;
colon :
double colon ::
underscore _
asterisk *
at sign @
hash sign #
open bracket / close bracket ( / )
open curly / close curly { / }
open square / close square [ / ]
new line ↡
one two three … nine 1 2 3 … 9

Spell code identifiers letter-by-letter and spaces are removed automatically:

  • "W H F A T" β†’ WHFAT
  • "W H forward slash F A T" β†’ WH/FAT
  • "one two three dash four five six" β†’ 123-456

Assistant mode

Hold mode (default):

  1. Hold Ctrl+Alt+R
  2. Speak a command
  3. Release - Writher processes and confirms

Toggle mode:

  1. Press Ctrl+Alt+R once to start recording
  2. Speak a command
  3. Press Ctrl+Alt+R again to stop - Writher processes and confirms

Example commands:

  • "Save a note: remember to buy milk"
  • "Create a shopping list: bread, eggs, butter, coffee"
  • "Add pasta to the shopping list"
  • "Appointment with the dentist tomorrow at 3pm"
  • "Remind me to call Marco in one hour"
  • "Show me my notes"
  • "Show my agenda"
  • "Delete the dentist appointment"
  • "Remove the shopping list"
  • "Delete the reminder about Marco"

System tray

Right-click the tray icon to access:

  • Notes & Agenda - open the notes/appointments/reminders viewer
  • Settings - configure recording mode (hold vs toggle), max recording duration, keyboard shortcuts, and microphone device
  • Quit - exit WritHer

Tip: Windows may hide the tray icon in the overflow area (the ^ arrow). To keep it always visible, go to Settings β†’ Personalization β†’ Taskbar β†’ Other system tray icons and enable WritHer.


Adding a language

  1. Open locales.py
  2. Add a new entry to the _STRINGS dictionary (copy "en" as a template)
  3. Set LANGUAGE in config.py to your language code

Project structure

writher/
β”œβ”€β”€ main.py              # Entry point and orchestrator
β”œβ”€β”€ config.py            # All user-configurable settings
β”œβ”€β”€ hotkey.py            # Dual-hotkey listener with hold/toggle modes (pynput)
β”œβ”€β”€ hotkey_util.py       # Hotkey serialisation, display names, and validation
β”œβ”€β”€ recorder.py          # Microphone recording (sounddevice)
β”œβ”€β”€ transcriber.py       # Speech-to-text (faster-whisper)
β”œβ”€β”€ replacements.py      # Two-layer post-processing (user vocab + opt-in symbols)
β”œβ”€β”€ injector.py          # Clipboard paste into active app (Win32 API)
β”œβ”€β”€ assistant.py         # Ollama LLM integration + function calling
β”œβ”€β”€ database.py          # SQLite storage (notes, appointments, reminders, settings)
β”œβ”€β”€ notifier.py          # Toast notifications + reminder/appointment scheduler
β”œβ”€β”€ widget.py            # Floating pill overlay with animated eyes
β”œβ”€β”€ notes_window.py      # Notes/Agenda/Reminders viewer window (CustomTkinter)
β”œβ”€β”€ settings_window.py   # Settings window (CustomTkinter)
β”œβ”€β”€ tray_icon.py         # System tray icon (pystray)
β”œβ”€β”€ brand.py             # "Pandora Blackboard" icon renderer
β”œβ”€β”€ theme.py             # Unified colour palette and font definitions
β”œβ”€β”€ locales.py           # i18n string tables (EN, IT, DE)
β”œβ”€β”€ paths.py             # Data-directory resolution (source vs frozen exe)
β”œβ”€β”€ logger.py            # Rotating file + console logger
β”œβ”€β”€ debug_keys.py        # Key event debugger utility
β”œβ”€β”€ test_delete.py       # Unit tests for voice-delete feature
β”œβ”€β”€ test_replacements.py # Regression tests for the two-layer replacement engine
β”œβ”€β”€ test_hotkey.py       # Hotkey serialisation + conflict detection tests
β”œβ”€β”€ requirements.txt     # Python dependencies
β”œβ”€β”€ img/
β”‚   └── logo_writher.png # Logo for README
└── LICENSE

Troubleshooting

AltGr not detected? Run python debug_keys.py to see exactly what pynput reports for your keyboard. Some keyboard layouts map AltGr differently.

Ollama not reachable? WritHer expects Ollama to be running locally. Start it with ollama serve (or via the installed Windows service), then verify the URL in Settings matches. When you trigger assistant mode with Ollama down, WritHer shows a toast notification and flashes the widget in the error state β€” dictation continues to work.

No audio / microphone not found? WritHer uses the system default input device unless you select a specific one in Settings. If the widget shows "🎀 No microphone detected", check your Windows sound settings. You can also open Settings from the tray and use the microphone dropdown to pick the correct device. Hit the ⟳ button to refresh the list if you just plugged in a new mic.

"No speech detected" but microphone works? This usually means Whisper received audio but couldn't recognize speech. Common causes:

  • Wrong input device selected (e.g. "Stereo Mix" instead of your actual mic) - check the microphone dropdown in Settings
  • Microphone volume too low in Windows sound settings (aim for 70-80%)
  • The default small model requires ~244 MB download on first launch; check the console for progress

Symbol substitution not working / weird output? Enable "Symbol & spelling mode" in Settings β€” it is off by default. For reliable multi-word phrases ("forward slash", "less than") set the Whisper model to small or larger; the base default is fast and accurate for prose but can mishear multi-word symbol names.

Custom vocabulary not applying? Layer A runs case-insensitively and matches whole words. If your spoken form contains a symbol or punctuation, add it exactly as Whisper transcribes it. Layer A is applied before Symbol & spelling mode, so vocabulary entries always win over the built-in substitutions.

Text not pasting? The injector uses Ctrl+V via the clipboard. Some apps with custom input handling may not respond. If injection fails, the text is saved to recovery_notes.txt so nothing is lost.

Tray icon not visible? Windows 11 hides new tray icons by default. Go to Settings β†’ Personalization β†’ Taskbar β†’ Other system tray icons and enable WritHer to keep it always visible.


License

MIT - see LICENSE for the full text.


Credits & Acknowledgements

Core architecture - voice dictation pipeline, Ollama assistant integration, floating widget, notes/agenda/reminders, tray icon - is by benmaster82 (this repository).

Contributions to upstream via pull request:

Contributor Contribution
LeikeBaus Unit test structure and coverage
LikeARealGinger Voice-delete by keyword, localized confirmations
Steven OhΓ‘d Appointment and reminder voice deletion, delete confirmation popup
Marcel Alsleben Assistant dispatcher refactoring, pending delete handling
Aaron Dutton OS-locale date/time formatting

The following features originate from the fork by @rusty-bit and have been integrated into upstream - their commits are preserved in this repository's history:

  • Combo hotkeys with live key capture (default assistant hotkey now Ctrl+Alt+R)
  • Per-mode widget accent colours (cyan for dictation, violet for assistant)
  • Log viewer in the Settings window
  • German locale
  • Single-instance lock (adapted to a per-session Local\ mutex here)
  • The spoken symbol / number substitution concept (redesigned here as the opt-in Layer B of replacements.py, with contraction safety and stricter spacing rules)

Contributing

WritHer is a young project and contributions are very welcome!

Here are the areas where help is most needed:

  • macOS port - replace Win32 injection and winotify with macOS equivalents
  • Linux port - same as above for Linux (xdotool, libnotify, etc.)
  • New languages - just add an entry to locales.py
  • Ollama model testing - report which models work best with function calling
  • Bug reports and UX feedback - open an issue, any feedback is appreciated

If you want to contribute, feel free to open an issue to discuss your idea first, or just fork and submit a PR. No formal process required, just good intentions.


Built with πŸŽ™οΈ faster-whisper Β· 🧠 Ollama Β· 🐍 Python

About

πŸŽ™οΈ Offline voice productivity for Windows - dictate text into any app and control a local AI assistant by voice. Manages notes, to-do lists, appointments & reminders. 100% local: faster-whisper + Ollama + SQLite. No cloud, no telemetry. Free and open source alternative to Wispr Flow

Topics

Resources

License

Contributing

Stars

72 stars

Watchers

2 watching

Forks

Packages

 
 
 

Contributors

Languages