Building Always On Top Timer: From Idea to Portable EXE

Project Link in GitHub: always_on_top_timer

Download v1.0.1 (Quick Steps)

  1. Open the repository link above.
  2. On the repo page, look at the right-side panel and click Releases.
  3. Open release v1.0.1.
  4. In Assets, download AlwaysOnTopTimer-v1.0.1-onefile.exe.
  5. Optional: download AlwaysOnTopTimer-v1.0.1-sha256.txt and 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 .exe with 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 point
  • timer_app.py: app flow and Win32 message handling
  • timer_model.py: shared timer state
  • timer_constants.py: Win32 constants
  • win32_types.py: ctypes structs and callback signatures
  • win32_api.py: WinAPI bindings and helpers

Runtime flow

  1. The entry point validates Windows runtime and starts the app.
  2. The app creates two native windows (control and display).
  3. A shared timer model tracks elapsed time.
  4. A Win32 message loop handles button clicks, resizing, hotkeys, and timer updates.
  5. Both windows redraw from the same state, so the displayed time stays synchronized.

Hotkeys

  • Ctrl+Shift+S: Start/Stop
  • Ctrl+Shift+R: Reset
  • Ctrl+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.