MouseTracks

A well-maintained desktop input-heatmap tool for games and productivity, evaluated as a reading-behaviour instrument and rejected — it records screen pixels, and a slide-viewing study needs image pixels at a known magnification.

Purpose

Not one of my repositories, and the recommendation is not to adopt it. Nothing has been run; there is no local_path because it was read from a scratch clone. The verdict is not a comment on its quality — it is a good tool, genuinely maintained, doing what it set out to do. It is the wrong instrument for this question.

It was evaluated against a specific alternative rather than in the abstract. The group already has a reading-behaviour instrument: the focus/ package in QuPath Patoloji Atlası Extension, which records blinded reader decisions plus dwell, scanpath and zoom behaviour inside QuPath, with saliency and ICC agreement analysis in R and Python. The decisive question was what MouseTracks does that that package cannot. The answer is narrow, and it does not include anything a slide-viewing study needs.

Provenance, from primary sources only: the owner is a single individual, GitHub huntfx, account type User, created 2014-12-18 — no organisational backing. His own GitHub profile self-describes as a Pipeline TD at an animation studio; that is self-reported and not independently corroborated, and nothing here depends on it.

Currency, read 2026-07-26: created 2017-03-07, 1,960 commits, 20 releases in the trailing twelve months (roughly one every two to three weeks), latest v2.6.0 on 2026-06-02. 173 stars and 25 forks, recorded as the weak signals they are. One small inconsistency worth not smoothing over: the GitHub API reports pushed_at 2026-07-13 while master HEAD in a fresh clone is 2026-07-08 — most likely a branch or tag push, not a discrepancy that matters.

Licence: GPL-3.0, with one thing to route rather than resolve. LICENSE.md’s operative text (Sections 0–17) is byte-identical to the canonical FSF text fetched from gnu.org; the only differences are non-operative URL protocols and the unfilled “how to apply” template. But the licence is declared in exactly one place, no source file carries a header notice, and the appendix notice was never filled in — so whether the grant is GPL-3.0-only or GPL-3.0-or-later is unstated. That election changes what a downstream project may do. [unverified], and only the repository owner can settle it. Separately, and independent of that question: GPL-3.0 is copyleft, so lifting code from here into anything the group distributes carries obligations that MIT-licensed QuPath Patoloji Atlası Extension does not.

Data used

None. Nothing has been installed or run.

For completeness, what it would capture if run: mouse position, keyboard, gamepad, and local network-bandwidth counters, written to %APPDATA%\MouseTracks\ on Windows. Profiles are .mtk files — plain zip archives of numpy arrays, np.save/np.load with allow_pickle=False, no password or encryption. Config and the application list are plaintext.

Methods

Python, two package generations: mousetracks/ (legacy) and mousetracks2/ (current, and what the installer ships). Mouse position is polled at 60 Hz and accumulated into per-pixel arrays, separated per monitor and per application profile; output is PNG heatmaps plus descriptive CSV.

Current state / open questions

Why it cannot answer a slide-viewing question — two independent structural reasons

1. It records screen pixels, and there is no path to image pixels. Cursor position comes from the raw OS cursor; coordinates are mapped only between DPI-scaled logical and physical desktop pixels, then offset by the tracked window’s on-screen rectangle. There is no concept of slide coordinates, magnification, zoom level, scroll offset or pan anywhere in the source. The only zoom in the codebase is scipy.ndimage.zoom, used to resize the finished heatmap image for output — unrelated to application content.

That is the whole verdict. A pathology reading study needs to know which part of the tissue was examined and at what magnification, because the same screen pixel means a different thing at 5× and at 40×. The focus/ package samples in image-pixel space with the viewer’s exact downsample factor recorded per tick; MouseTracks cannot express the question, let alone answer it. This is structural, not a missing feature.

2. Visit order is partly destroyed, so scanpath analysis is out. Worth stating precisely, because the obvious summary is wrong. Three arrays are written per pixel (mousetracks2/components/processing.py, ~line 198):

  • density_arrays[...] += 1 — visit counts do accumulate.
  • sequential_arrays[...] = data.counter — a monotonic tick counter, overwritten on every revisit, so only the most recent visit time survives per pixel.
  • speed_arrays[...] = max(...) — peak speed.

So a lossy recency ordering does survive — enough to render a trail image — but revisits are lost. Fixation detection needs dwell episodes over time, and transition matrices and path-similarity measures need the full sequence including returns. None of those can be recovered from a per-pixel last-visit stamp. Combined with (1), no amount of post-processing on the saved arrays reaches the analysis the group already has.

3. It computes no saliency or agreement statistics at all. A repository-wide search across both packages for KL/JS divergence, NSS, AUC, ICC, IoU, kappa, fixation, gaze, entropy or pairwise correlation returns zero matches. Output is heatmap PNGs and descriptive CSV counts and durations.

Is anything worth lifting? Checked rather than assumed, because that has been the useful salvage on other evaluations. The .mtk arrays are extractable, and render.py does build a normalised, Gaussian-smoothed density surface — which is the input object saliency metrics consume. But normalise_array is four lines and gaussian_size is two; the rest of that 551-line file is colour mapping and output plumbing. There is no equivalent here of the ~50-line survival loss that made MultiSurv worth reading. Nothing to lift.

What it genuinely does better, stated plainly

One capability, and it is real: zero-integration, whole-desktop, cross-application capture. It needs no cooperation from the application being observed. The focus/ package only sees inside QuPath and structurally cannot see anything else.

So there is one question MouseTracks answers that the in-house instrument cannot: how does a pathologist divide time across QuPath, the LIS, the PACS viewer and email during a reporting session? That is a workload and interruption question rather than a slide-reading one — closer to Turnaround Time and Laboratory Workload Measurement than to Interobserver Agreement — and nothing in this wiki currently measures it. If that question is ever asked, this tool is a reasonable starting point and the concerns below still apply.

Why it should not go on a clinical workstation

These are independent of the capability gap, and they are what would decide it even if the capability fit.

The keyboard finding, calibrated — and two of my own first-pass claims were too strong. It is not a content keylogger: each keystroke is converted immediately to a numeric virtual-keycode and only per-keycode occurrence counts are persisted — no text, no ordering, no per-event timestamps, so typed content cannot be reconstructed from the saved data. Adversarial checking corrected two overstatements I made on the first pass: “the raw key object is never retained anywhere in memory” fails in an edge case where pynput supplies no VK code (unreachable for a physical keyboard on Windows), and the counter array auto-pads rather than being a fixed 255 slots. What stands, and matters:

  • Letter-level key identity is captured (VK_A distinguishable from VK_B), just not order.
  • The global OS-level keyboard hook is installed unconditionally at startup, regardless of the track_keyboard setting or the --disable-keyboard flag. Those only gate whether the resulting keycode is counted downstream — the system-wide hook still receives every keystroke.
  • Every keypress is printed by name to a live console.
  • The legacy package additionally records digraph key-pair timing histograms and backspace-adjacency counts — that is keystroke dynamics, a materially different and more sensitive risk class. It is not what the installer ships, but it is launchable from the same repository.

No screen capture anywhere. Exhaustive search across both packages for screenshot, OCR and framebuffer APIs returns nothing. Worth crediting explicitly. One caveat: the macOS build ships pyobjc-framework-Quartz — the framework that could capture — because reading window titles on recent macOS requires Screen Recording permission.

Two README claims that the code contradicts, which is exactly the discrepancy worth recording because the next reader will also read the README:

  • The README says networking “can be completely disabled with the --offline flag.” It cannot. The About dialog and a recurring ten-minute GUI timer call the update check without ever consulting the offline flag, so it contacts api.github.com on a schedule regardless (capped to roughly hourly per process by an internal cache). In fairness: it is a bare unauthenticated GET and no captured tracking data is ever transmitted — verified, there is no telemetry, analytics or upload path anywhere in the tree.
  • The README says “Each release is digitally signed and verified to prevent tampering.” That describes a project-private Ed25519 scheme checked only by MouseTracks’ own launcher — not an OS- or CA-trusted signature. There is no Authenticode step and no Apple codesign; the README itself admits the macOS build is unsigned. The check is Windows-only in CI and client-side stubbed to always pass on Linux and macOS, and no checksums are published.

Installed copies self-update by default, in a background thread on launch and periodically after, downloading executables from GitHub Releases. Combined with unsigned binaries built and published automatically from a personal account, vetting one binary means accepting an unaudited update channel. The Windows installer installs per-user without admin rights and registers autostart in the user’s registry hive, so it can be installed and made persistent on a managed machine by someone with no elevated privileges.

Uninstalling does not remove the data. The uninstaller deletes only versioned executables from the install folder; every recorded profile in %APPDATA%\MouseTracks persists indefinitely unless deleted by hand.

One narrow exposure surface worth naming, because it generalises past this tool: window titles are read via GetWindowTextW, printed to the console and shown in a GUI label. They are not persisted to the .mtk file — so this is an on-screen and in-console exposure, not a data-at-rest one. On a workstation whose PACS or LIS window title carries a patient name, that is enough to matter during screen sharing or a recorded observer session. See De-identification, which now carries this as a fourth surface.

Open questions

  • Is the GPL-3.0 grant “only” or “or later”? Unstated in the repository; the owner is the only one who can say. [unverified]
  • Does the group actually want the cross-application workload question answered? That is the one thing this tool is genuinely good for, and nothing in this wiki currently asks it.
  • The legacy package runs a local Flask control API on by default and an optional TCP stream server, both loopback-bound. That surface was not read in depth because the current package does not use it. [unverified] — it would need reading before anyone ran launch-legacy.

Related: QuPath Patoloji Atlası Extension — the incumbent instrument this was measured against, and where the verdict is written back.

Related: Reading-Behaviour Capture — the two-property pre-adoption test this evaluation produced, which is the durable output rather than this page.

Related: Interobserver Agreement — the studies a reading-behaviour instrument exists to serve, and the reason a second, weaker instrument is not what is needed.

Related: De-identification — window titles as an on-screen identifier surface, added there because of this reading.

Derived from: repository source read 2026-07-26 at commit 2234122 (2026-07-08) — mousetracks2/{components/{tracking,processing,app_detection}.py, utils/{input,keycodes,update,monitor}.py, file.py, render.py, config.py, applications.py, constants.py}, mousetracks/track/background.py for the legacy divergence, LICENSE.md diffed against the canonical FSF text, README.md, MouseTracks.spec, MouseTracks.iss, .github/workflows/build-executable.yml, plus the GitHub REST API for provenance, releases and currency. Comparison read against D:/patolojiatlasi-QuPath focus/ and analysis/.