Native Desktop Widget

pixelbar

A Rainmeter-style status column for the edge of my Windows desktop: eleven widgets in white 1-bit pixel art, from a dot-matrix clock and dithered album covers to what every Claude Code and Codex session is doing, drawn by one native C++ window for a fraction of a percent of the machine.

C++20
Win32
C++/WinRT
DXGI
Windows
Live: Runs on my desktopCode
The pixelbar column in two halves, populated with demo data: dot-matrix clock, year grid, media player, agent sessions, usage limits, AI usage, Slack, calendar, weather, system and processes

Employer signal

What This Project Shows

A native Windows program that runs all day beside everything else without showing up in Task Manager: Win32 windowing, WinRT media, DXGI frame pacing and log parsing across files of hundreds of megabytes, held to a strict pixel-art standard. It is also the clearest example of how I work with an AI implementer. Claude Code wrote the code from my brief over two days; I set the design rules, judged every build on the real desktop, and made each number on screen survive a second, independent measurement before I believed it.

Problem

What Needed To Be Solved

The leftmost of my three monitors sits on a plain black wallpaper, and I wanted it to carry a column of status in white pixel art: music with real controls, system load, and above all the state of my AI coding sessions. That last part is the hard one. Neither Claude Code nor the Codex side of the ChatGPT desktop app tells other programs what a session is doing, so which session is working, finished, or waiting on me has to be inferred from the logs they write: files that run to hundreds of megabytes and are being appended to while they are read. A pending permission prompt never reaches Claude's transcript at all. A widget engine built on counters and gauges has no way to know any of it.

Approach

How I Built The Solution

Three rules shaped it. Native and small: one layered Win32 window drawn pixel by pixel into a premultiplied buffer, so the column never competes with the apps beside it. Derived rather than polled: everything on screen comes from what the system and the apps already write, only the weather, the calendar and Slack touch the network, and their secrets live in Windows Credential Manager rather than the config. And pixel art first: one hand-drawn 5x7 font at 1:1, dot-matrix renderings for the big numbers, 1-bit dithering for album covers, dense rows, bars that span the full width, and motion that is quick rather than floaty.

Outcome

What It Demonstrates

It runs all day on my left monitor: about 6,100 lines of C++ in 15 files, built into a 714 KB executable that holds eleven widgets at about 1.6% of one core while something animates and under 30 MB of memory, started at sign-in and controlled from any prompt with meter start and meter stop. The code is public, with the README's images rendered from demo data. The honest gaps: there is no automated test suite (verification is rendered frames, frame-by-frame recordings and an independent recount, each repeatable from the command line rather than in a harness), Win+D handling is written but has never been exercised, a Codex session waiting on an approval still reads as working because that prompt never reaches its log, and GPU detail needs an NVIDIA card. It was built in about two days with Claude Code as the implementer; my part was the brief, the design rules, the judgement on every build, and the insistence that each number be checked by a second measurement. The judgement I would claim from it is that last habit: the token table, the frame pacing and the footer all looked believable before they were checked, and two of the three were wrong.

Screens

The column launching with demo data: widgets dissolve in through an ordered dither from the top down, then the column springs open as weather, calendar, Slack and token counts arrive
Launch, recorded frame by frame from --demo. Each widget dissolves in through an ordered dither, top to bottom, then the column springs open as the weather, calendar, Slack and token counts arrive.
The settled column with demo data: clock, year grid, media, agent sessions, usage limits, AI usage, Slack, calendar, weather, system and processes
The settled column. One hand-drawn 5x7 font at 1:1, dot-matrix digits for the clock and the temperature, and bars that span the full width for every core and every hour.
The settings panel: widget order with checkboxes and arrows, then clock, weather, agent, calendar, Slack, process and display options
Settings live in the column itself: widget order, per-widget options, and the two secrets, which are pasted from the clipboard into Windows Credential Manager and never shown.

Evidence From Source

A font that lost the pixel feel

The first readability pass traced the 5x7 glyphs at double resolution. On the black desktop it read as thin vector strokes and lost the look the project existed for, so it was reverted: the font stays native at 1:1, and the only large elements, the clock and the temperature, became dot-matrix renderings of the same glyphs, which keeps big numbers pixel art without blocky 4 px squares.

Token counts against an independent recount

The AI usage table was checked against a separate Python recount of the same logs. Calls and tokens matched exactly at the moment of comparison (Claude 195 calls, Codex 129), and the recount exposed a bug the table alone could not: compaction summaries and the app's injected task notifications were being counted as turns I had typed. Turns now exclude subagent briefs, compaction summaries and any tagged system message.

Motion measured, not eyeballed

The --record mode writes every composed frame with its timestamp. The first recording showed gaps anywhere from 0 to 35 ms between frames, because renders came from a coarse timer and from every data update. After motion moved onto the vertical-blank pump the gaps were a steady 20 ms, which looked wrong until a display query showed that monitor runs at 50 Hz: exactly one frame per refresh. The same recordings show each fill-in landing in about 200 ms with no overshoot.

A column that died with the Claude app

After a Claude desktop restart the column was gone. It had been started from an agent's shell, inside the app's process job, and went down with it. Launches now go through Explorer, a job-membership query confirmed the new process sits outside the agent's job, and the same launch path went into the build script and the meter start command.

A 200 with nothing in it

Weather replies came back 200 OK with zero bytes, then a read error (E_ABORT). The cause was WinHTTP's automatic decompression of gzip replies; with it removed and the body read until the stream ends, the same requests parse.

Events on the wrong day

All-day calendar events landed on the wrong day because ICU's default zone inside the process came out as UTC. Floating and all-day times now go through Windows' own local-time rules, and an unknown TZID falls back to local time rather than UTC. A synthetic calendar with weekday rules, a UTC event, all-day events, exceptions, a counted daily rule with a moved instance and a cancelled event was used to check the expansion.

Its own cost, on screen

The footer shows the column's own CPU and memory, measured the way Task Manager measures every other process. Against Windows' performance counters in the same seconds it read 27 MB to their 27.5 MB, and between 0.0% and 0.4% of the machine.