Building Always On Top Timer: From Idea to Portable EXE
Project Link in GitHub: always_on_top_timer
Download v1.0.1 (Quick Steps)
- Open the repository link above.
- On the repo page, look at the right-side panel and click Releases.
- Open release v1.0.1.
- In Assets, download
AlwaysOnTopTimer-v1.0.1-onefile.exe. - Optional: download
AlwaysOnTopTimer-v1.0.1-sha256.txtand verify the file hash.
Quick Overview
I wanted a lightweight Windows timer that would always stay visible, be easy to control with hotkeys, and run without a heavy UI framework.
This project became always_on_top_timer: a native Win32 desktop timer built in Python, packaged as a portable executable, and released through GitHub Actions.
Why I Built It
Most timer apps were either too heavy, not truly always-on-top, or awkward for second-screen use.
The goals were simple:
- Keep the app small and fast.
- Make it easy to see from a distance.
- Add keyboard-first controls.
- Ship a portable
.exewith no installer.
The Build Journey (High Level)
1. Start with core timer behavior
First, I focused on reliable timer state and controls:
- Start/Stop
- Reset
- Accurate elapsed time using monotonic timing
2. Move to native Win32 UI
To keep the app lightweight and Windows-native, I used Python ctypes with Win32 APIs instead of a larger UI toolkit.
This gave full control over:
- Always-on-top windows
- Native buttons and rendering
- Global hotkeys
- Window behavior and styling
3. Split the code into clear modules
As features grew, I separated responsibilities so the code stayed maintainable:
- entrypoint
- app/controller logic
- timer model
- constants
- Win32 bindings and types
4. Add dual-window UX
I implemented two synchronized windows:
- A control window (Start/Stop + Reset)
- A display-only timer window
Both read from the same timer state, so they always match.
5. Package as portable executable
I added a build script with PyInstaller to produce:
- one-folder output (development and fallback)
- one-file output (recommended release artifact)
6. Automate release with GitHub Actions
I set up two workflows:
- CI workflow for pull requests and main pushes
- Release workflow for version tags (for example
v1.0.0,v1.0.1)
The release workflow builds the one-file executable, generates checksums, and publishes assets to GitHub Releases.
How It Works
Architecture
always_on_top_timer.py: entry pointtimer_app.py: app flow and Win32 message handlingtimer_model.py: shared timer statetimer_constants.py: Win32 constantswin32_types.py: ctypes structs and callback signatureswin32_api.py: WinAPI bindings and helpers
Runtime flow
- The entry point validates Windows runtime and starts the app.
- The app creates two native windows (control and display).
- A shared timer model tracks elapsed time.
- A Win32 message loop handles button clicks, resizing, hotkeys, and timer updates.
- Both windows redraw from the same state, so the displayed time stays synchronized.
Hotkeys
Ctrl+Shift+S: Start/StopCtrl+Shift+R: ResetCtrl+Shift+Q: Quit
What Made the Biggest Difference
- Choosing native Win32 for a lightweight app footprint.
- Separating model, UI flow, constants, and API bindings.
- Building release automation early to avoid manual packaging.
- Publishing checksum files with each release for trust and verification.
Current Result
The app now supports:
- Native always-on-top Windows experience
- Synchronized control and display windows
- Global hotkeys
- Portable one-file executable releases
- Automated CI and tag-based release publishing
Closing
This project started as a small utility idea and evolved into a full, reproducible desktop release pipeline.
The main lesson: small desktop tools become much easier to maintain when architecture and release automation are treated as first-class work from the start.