Building Sensor Readout's Process Watcher

How a GitHub issue became a polished Sensor Readout 4.10.0 feature.

The Request

The feature began with a GitHub issue asking whether Sensor Readout could watch an app's behaviour. On the surface that sounds simple: pick a process, watch some counters, write a file. But with Sensor Readout, simple ideas usually need to pass a few tests before they belong in the program.

Does it fit the product? Is it useful for support? Is it accessible from the keyboard? Does it give a screen-reader user the same confidence a sighted user gets from seeing the UI? Does it respect privacy? Does it stay out of the way while the user does the thing they are trying to measure?

Andre said: There is a new issue. Is this a good fit, or should we gently close it?

I thought: It is a good fit if we keep it as observation, not control. Sensor Readout should not become a full task manager. It should help someone prove what happened on a machine.

I did: I scoped the first pass around a process watcher: choose one running process, collect resource counters, and save a support report.

That first decision mattered. If we had started by trying to clone Task Manager, the feature would have sprawled. Instead, the first version had a clear centre: watch one process and produce evidence.

The First Build

The first working shape was plain and practical. A user could open a Watch Process dialog, choose a process, start watching, and then stop it later. It could write a report. It proved that Windows exposed enough information to make the idea worthwhile.

It also proved that "working" is not the same thing as "finished."

Andre said: "I love the idea, it works well, but I think it should be html in table form not text. It makes it very difficult to hear/understand/parse with a screen-reader."

I thought: He is right. A text file is easy to generate, but a dense text report is not necessarily easy to use. Tables and headings are structure. Structure matters.

I did: I changed the watcher report flow so HTML became the natural output, with CSV available for people who want spreadsheet data.

This is a common pattern in Sensor Readout work. The first pass proves the capability. The second pass asks whether the capability is pleasant to use with NVDA, JAWS, a braille display, or no screen reader at all. The answer often changes the design.

No Save Dialog

The next iteration was about flow. If someone is diagnosing a problem, they may want to watch several processes one after another. A Save dialog after every run sounds harmless until you actually have to do it repeatedly.

Andre said: "Actually I think we don't offer the user a save dialog at all. What you set in the main window is what happens... What if user wants to watch a series of processes one after the other?"

I thought: Sensor Readout already has a Reports folder. The process name and date can make a useful filename. Asking every time is unnecessary friction.

I did: I made the watcher save automatically to Reports, using the selected process name and timestamp.

That made the feature feel more like Sensor Readout and less like a generic utility bolted onto the side. The user sets the behaviour once, then the program gets out of the way.

Getting Out Of The User's Way

The first dialog still had a deeper problem: it stayed in front. That makes no sense for a watcher. If the goal is to measure what happens while someone uses a program, Sensor Readout should not force itself to be the program in focus.

Andre said: "You may want to actually use the machine while it's running, so you should be able to still background SR when it's in-use."

I thought: Exactly. A watcher dialog that demands attention changes the test. The feature needs to run quietly in the background.

I did: Start now closes the dialog, starts the watch in the background, and lets Sensor Readout return to normal use. Timed watches can stop automatically; indefinite watches can be stopped from the menu or shortcut.

This was also when speech feedback became important. If a user starts a timed watch from a shortcut or context menu, they need to hear what just happened.

That is small wording, but it prevents uncertainty. It tells the user whether they can walk away and wait, or whether they need to stop the watch manually.

The Dialog Became A Better Process List

Improving the watcher dialog created an unexpected opportunity. To help people choose the right process, the picker started showing more context: process name, window title where Windows exposes one, process role for multi-process apps, start time, and parent process.

Once that existed, hiding it inside a temporary dialog felt wasteful.

Andre said: "The order on-screen is so screen-reader-friendly now, it feels a shame to just leave it in one section."

Andre then said: "Can the main screen look like the watcher? I really like knowing where something came from."

I thought: This belongs in the Tasks category. The watcher picker had accidentally become a better lightweight process browser.

I did: I added a read-only Running Processes tree under Tasks, while keeping the existing highest CPU, highest memory, highest GPU, and highest GPU memory summary rows for spoken hotkeys.

That changed the feature from "a dialog you use sometimes" into "part of Sensor Readout's normal view of the system." A user can now browse running processes, open Details, open the file location, or start a watch directly from a process row.

The First Real Bug: It Was Too Slow

The new Tasks process list was useful, but it had a nasty first flaw. Arrowing through it was sluggish. That is the kind of problem that can kill a feature for a screen-reader user. If every arrow press feels delayed, the information may as well be hidden.

Andre said: "For some reason it's incredibly sluggish arrowing through the process list even though you said it doesn't refresh quickly."

I thought: The refresh interval is only part of the story. If selection, context menu checks, or row rebuilding are doing process metadata work, navigation will still feel heavy.

I did: I separated the fast summary rows from the full process inventory and cached the expensive process list. The full inventory refreshes calmly, not every time the user moves through it.

After that, the process list became fast. That was a turning point. A useful feature that is slow feels fragile. A useful feature that is fast feels native.

Finding The Right Process

Process names alone are not enough. Modern apps often run many processes with the same name. Chrome, Discord, Dropbox, ChatGPT, Electron apps, and developer tools can all have several rows that look nearly identical if you only show a name and PID.

Andre said: "If I wanted to watch you work in this codex thread, I'd have no idea which one to watch at all."

I thought: PID is useful for technicians but not friendly enough as the main clue. Start time, window title, role, and parent process are more helpful for choosing.

I did: The visible process rows now prefer user-facing context. PID remains in Details, where it belongs.

This is where the feature started to feel genuinely polished. It was not just collecting counters; it was helping the user decide what to collect.

Search Had To Follow The New Shape

Sensor Readout already had F3 search, so I did not want to invent a second search just for Tasks. But the process work exposed a weakness: sometimes the thing you want to search for is not visible in the row. It is in Details.

Andre said: "Do we not already search with F3 anyway? do we need something extra for this?"

I thought: We do not need a new search. We need the existing search to be smarter.

I did: F3 now returns visible-text matches first, then detail-only matches. That keeps ordinary search predictable while still allowing deeper finds.

That means a search for a process name behaves normally, while a search for an executable path, parent process, device identifier, process role, or other hidden detail can still land on the right row.

Details Needed To Tell The Same Story

One test exposed a mismatch. A process row could say it was a GPU process, but Details did not show any GPU counters. That is the sort of inconsistency that makes users question the entire report.

Andre said: "When I go to details, it doesn't mention GPU anywhere, each details section contains the same thing for all apps. Should we be displaying any more info here?"

I thought: If Windows exposes process GPU counters, Details should show them. If it does not, Sensor Readout should stay honest and not invent anything.

I did: Process Details now include current GPU usage, dedicated GPU memory, and shared GPU memory where Windows exposes those counters.

That follows a Sensor Readout rule that has become more important as the app grows: show what we can read truthfully, and do not pretend about the rest.

The Privacy Boundary

A process watcher can sound worrying if described badly. So the manual and release wording had to be clear. Sensor Readout is not looking inside anyone's private data.

The final boundary is simple:

That distinction matters. This is a support feature, not a surveillance feature.

The Manual Had To Catch Up

Once the feature had two entry points, the manual needed careful wording. There is a difference between opening the Watch Process dialog and starting a watch from a process row.

Andre said: "Make the watcher more clear, explain that watching a running process uses your previous settings so if you'd rather not do that, set it up fresh from the actual watch menu instead."

I thought: That is not a footnote. It is the difference between deliberate setup and quick action.

I did: The manual now explains that Options > Watch process... is where you set duration, format, speech, and sound. Watching directly from Tasks uses those remembered settings immediately.

The changelog also gained the F3 search improvement because it is not just an internal tweak. Users will notice that search finds more useful things.

The Release Checks

The final push was not just "compile and upload." Sensor Readout has picked up a fairly strict release routine because small release mistakes have caused trouble before. This time the release checks covered:

The checks caught small things before release: a language fallback false positive and generated manual file ending issues. Those are boring problems, but boring problems are exactly what release scripts are supposed to catch.

The Final Push

By the end, the feature was no longer just "watch a process." Sensor Readout 4.10.0 shipped with:

The release went live as Sensor Readout 4.10.0. The public update path passed, the program and source ZIPs were attached to the GitHub release, and issue 21 was closed with a note explaining what had shipped and why the feature stayed focused on observation rather than becoming a full task manager.

What This Shows

The interesting part of this feature is not that it compiled. The interesting part is how many times it changed shape because of testing.

Andre would try it with NVDA, in the real app, with real processes, and then point out what did not feel right. I would change the implementation. He would test again. Sometimes the next problem was obvious. Sometimes the fix created a better idea. The process picker became a process list. The process list became part of Tasks. The watcher report became HTML. The quick action inherited saved watcher settings. Search became deeper. Details became more honest.

That is the difference between a feature that exists and a feature that belongs. A workable skeleton is useful, but only as a starting point. The polish comes from listening to the user, testing the thing in the way it will actually be used, and being willing to reshape the design instead of defending the first version.

Release Result

Sensor Readout 4.10.0 was published to GitHub, the post-publish updater smoke passed, and GitHub issue 21 was closed. The finished feature is a focused, accessible process observation tool: practical enough for support, careful enough about privacy, and polished enough to feel like it was always meant to be part of Sensor Readout.