If you've ever built a desktop app in Python, you've probably used Tkinter. And if you have, you probably think it's doomed to look like a clunky, grey Windows 95 application.
I thought so too.
But recently, I needed a lightweight, floating UI for a 100% local, offline voice assistant I was building. I absolutely refused to bundle an entire Chromium instance (Electron) just to render a small widget.
So I decided to push Tkinter to its absolute limits. The result is Writher: an open-source, privacy-first voice assistant and dictation tool powered by faster-whisper and Ollama.
In this post, I'll break down the tricks I used to make a legacy Python GUI look modern, and the architecture behind a fully local AI desktop app.
🎨 The UI Hack: Making Tkinter Beautiful
To get that modern, glassmorphic floating pill shape with glowing borders, I completely bypassed Tkinter's standard widgets.
Instead, I used PIL (Pillow) to render high-resolution graphics dynamically on a transparent Tkinter Canvas.
The Trick: Borderless & Transparent Windows
# Remove the window frame entirely
root.overrideredirect(True)
# Chromakey hack: make a specific color fully transparent
root.wm_attributes("-transparentcolor", "#000001")
This gives you a frameless, floating window — the foundation for any modern-looking widget.
Dynamic Glow Rendering
Every frame of the animation (the bot's eyes changing expressions: listening, thinking, happy) is drawn on-the-fly using ImageDraw and ImageFilter.GaussianBlur to create a glowing effect that mimics SVG filters:
from PIL import Image, ImageDraw, ImageFilter
def draw_glow(size, color, blur_radius=10):
img = Image.new("RGBA", size, (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
draw.rounded_rectangle(
[blur_radius, blur_radius,
size[0] - blur_radius, size[1] - blur_radius],
radius=20, fill=color
)
return img.filter(ImageFilter.GaussianBlur(blur_radius))
The Ghost Window
Since Writher is a dictation tool, clicking it shouldn't steal focus from your active app (like VSCode or a text editor). I used Win32 ctypes to apply the WS_EX_NOACTIVATE style:
import ctypes
GWL_EXSTYLE = -20
WS_EX_NOACTIVATE = 0x08000000
WS_EX_TOOLWINDOW = 0x00000080
def make_ghost_window(hwnd):
style = ctypes.windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
ctypes.windll.user32.SetWindowLongW(
hwnd, GWL_EXSTYLE,
style | WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW
)
💡 Pro tip:
WS_EX_TOOLWINDOWalso hides the window from the Alt+Tab menu, making it behave like a true desktop widget.
🧠 The Brain: 100% Local AI
I'm tired of sending my voice and private notes to the cloud. Writher operates entirely on your local machine.
Speech-to-Text — I used faster-whisper (CTranslate2). It runs flawlessly on CPU or GPU and transcribes voice in near real-time.
The LLM — I hooked the app to Ollama. Press Ctrl+R, Writher listens, transcribes, and passes the text to a local model for processing.
Function Calling — This is where it gets interesting. Instead of just chatting, I configured the LLM with tool definitions. It converts your voice commands into structured function calls — saving notes, scheduling appointments, or setting reminders in a local SQLite database:
# Thread-safe SQLite with WAL mode
conn = sqlite3.connect("writher.db")
conn.execute("PRAGMA journal_mode=WAL")
The LLM doesn't just understand you — it acts on what you say.
⚙️ The Muscle: Seamless OS Integration
Most dictation tools copy text to your clipboard and force you to paste manually. Writher acts like a phantom keyboard.
I initially used pyperclip, but it suffers from race conditions when other apps lock the clipboard. To make it bulletproof, I wrote a custom clipboard injector using the Win32 API (OpenClipboard, SetClipboardData).
The flow:
- Save your current clipboard contents
-
Inject the transcribed text and simulate
Ctrl+Vviapynput - Restore your original clipboard — all in milliseconds
And just in case Windows blocks the clipboard? I implemented a fail-safe that automatically appends your dictation to a recovery_notes.txt file. You never lose a single word.
🔑 What I Learned
Building Writher taught me a few things I wasn't expecting:
- You don't need Electron for everything. A transparent Tkinter canvas + Pillow can produce surprisingly polished UIs. The binary is tiny compared to any web-based alternative.
-
Local AI is ready. With
faster-whisper+ Ollama, you can build genuinely useful AI tools that never phone home. Privacy doesn't have to mean sacrificing quality. - Win32 APIs are your secret weapon on Windows. Ghost windows, clipboard control, focus management — they unlock capabilities that pure Python can't reach alone.
🚀 Try It Yourself
The whole project is open-source. You can check out the UI implementation, the AI pipeline, and run it on your own machine:
I'd love to hear your thoughts. Have you ever pushed a legacy GUI framework beyond what it was meant to do? What local LLM setup are you running? Drop a comment — I read all of them.
And if you find the project useful, a ⭐ on the repo helps more than you'd think. 🙏
Top comments (0)