Product: Proof of Work — Why My Desktop Timer Was Burning CPU and How I Fixed It
I ship a small desktop productivity tool for time management: a local timer that sits in the system tray, starts a work block, and ends it when the time is up. It is meant to make desktop productivity faster, not noisier. For two weeks, it failed that test.

The bottleneck was not the timer; it was the repaint
The first sign was battery. On a 2019 Dell Latitude, the widget ran for eight hours and drained 11% more than the same day without it. The fan spun up during normal typing. That is not acceptable for a tool that is supposed to save time.
I opened the profiler and watched the main thread for one minute. The timer logic took 2.1 ms. The rest was waste.
What the profiler showed before the fix
- 14.2 ms per second from a progress ring repaint.
- 4.6 ms per second from IPC messages sent between the UI and the background process.
- 1.9 ms per second from a blur listener that fired on every window change.
- 0.4 ms per second from the actual countdown check.
The product was not slow because the math was bad. It was slow because it painted, messaged, and listened more often than it needed to.
The fix was smaller, not smarter
I did not rewrite the product. I removed the parts that did not earn their cost.
Code tweaks that removed the waste
I replaced a one-second setInterval with a CSS animation for the progress ring. The animation uses a compositor-friendly transform. JavaScript only updates the label when the minute changes.
const now = Date.now();
if (now >= session.endsAt) {
finish();
} else if (Math.floor(now / 60000) !== lastMinute) {
updateLabel();
}
That one change removed the per-second DOM write.
I cut the IPC payload from 320 bytes to 28 bytes. The UI no longer receives the full session object. It receives only the state, the expiry timestamp, and the window ID.
I removed the blur listener from the webview. The widget now uses OS-level focus events only when the user starts or ends a block.
Hardware and rendering adjustments
The remaining cost was the window itself. I changed the widget from a semi-transparent, shadowed window to an opaque, borderless window. I disabled window shadows and reduced alpha blending. On the same laptop, the integrated GPU stopped doing extra work for a tool that only needs to show a countdown.
I also set the background process to low priority. The timer still fires on time, but it no longer competes with the browser, the terminal, or the IDE.

The benchmark: 18.6 ms to 0.4 ms
I ran the same eight-hour test on the same machine. I used the same browser, the same terminal, and the same timer settings.
Before the fix: - 18.6 ms of main-thread work per second. - 11% extra battery drain over eight hours. - 12 fan spin-ups during normal typing. - 41 minutes of daily fiddling with the widget, based on my own usage log.
After the fix: - 0.4 ms of main-thread work per second. - 3% extra battery drain over eight hours. - 0 fan spin-ups during normal typing. - 9 minutes of daily fiddling with the widget.
The timer drift after a 12-hour run was 118 ms. That is not perfect, but it does not matter for a time management tool that starts and ends work blocks. The user does not need a stopwatch. They need a reliable boundary.

What the numbers mean for a desktop user
A desktop productivity tool should not make the machine feel worse. If a focus timer causes fan noise, it has already cost more attention than it saved. The fix was not a new feature. It was a smaller product: fewer repaints, fewer messages, fewer listeners, and a window that costs less to draw.
That is 32 minutes back per day, and it comes from removing waste, not from asking you to focus harder. The goal was efficiency, not decoration. If you want the exact patch, the changelog has it. The lesson is simple: measure the machine before you blame the habit.