Releases: PrimeBuild-pc/MouseLatencyTester
Release list
MouseLatencyTester 1.2.0 — Probe-to-Photon
The hardware is finished, and there is a second way to measure.
Probe-to-Photon
A separate measurement mode, firmware v1.5. t₀ is the same probe contact on D2, taken in the same interrupt as always; t₁ is the first KY-018 reading past a calibrated threshold. The dashboard shows a large black target in the centre of the screen and flips it to white the instant Windows reports the click — so the number covers the mouse, the link, the OS input stack, the application's repaint, the compositor, the GPU queue and the panel.
Measured on real hardware: 40 clicks, ~16 ms median, against ~3 ms for the same mouse in Probe-to-PC.
Warning
The KY-018 is a photoresistor. Its own response time is in the milliseconds — the same order as what is being measured — and it is part of every figure this mode reports. Use Probe-to-Photon to compare one setup against itself. It is not an absolute click-to-photon benchmark, and the ~13 ms difference above is the display pipeline and the sensor together, not "my monitor adds 13 ms".
The calibration is automatic and happens on the target itself: black, settle, sample, white, settle, sample. Baselines closer than 60 ADC counts refuse to arm and the window stays open with a live light reading so the sensor can be aimed by watching a number instead of guessing.
The two modes never mix. Different wire token, separate firmware statistics, runs.mode in the archive, and the Compare tab says so when a comparison spans both.
The buttons do something
BTN1 enters and leaves test mode, BTN2 clears the live run outside it, after the 10-press acceptance test passed on hardware. The firmware still only reports a press; the dashboard decides and refuses one that would disturb a run. New preference, on by default: ask before BTN2 clears a run.
Fixed
- The live tab's right-hand column scrolls. With the optical panel showing it was taller than a laptop screen, which put Enter test mode below the bottom edge of the window.
- "Access denied" on the COM port at startup: the serial reader thread is joined on close so Windows really releases the handle, and the automatic connection retries quietly instead of opening a modal.
Hardware: settled
Every component is built, wired and verified. The 2N2222A and the 220 Ω resistors are not needed — they were on an early parts list and never found a role. If you bought the kit, leave them in the drawer.
The mouse is still never touched: no opening it, no soldering to its PCB, no transistor across its microswitch. Removable copper tape on the outside of the button remains the only mouse-side modification.
Next up is a 3D-printed enclosure, including a mount to hold the probe steady — probe alignment is the largest error source in the whole method.
Upgrading
- Flash
firmware/latency_tester_photon_v1_5/. The Probe-to-PC measurement path is byte-identical to v1.4 and v1.3, so your existing runs stay comparable. - The archive migrates itself to schema v3 in place. Existing runs read as
probe_to_pc— the only mode that existed when they were written. Nothing is deleted. - 293 tests, 88% coverage of the non-GUI code, green on Windows and Linux across Python 3.10 and 3.12.
The installer is not code-signed, so SmartScreen warns on first run. Verify SHA256SUMS.txt.
MouseLatencyTester 1.1.0
Knowing which mouse is plugged in — without guessing.
Install
Download LatencyTester-1.1.0-Setup.exe below, or use Settings → Check for updates if you already have 1.0.0.
Verify against SHA256SUMS.txt:
2E857CC0623B01330956A15EEDE558319673110A80D4E455C418B87228E9AFC3
Mouse detection
Devices → Detect connected mouse reads the USB string descriptors and fills in the profile name, manufacturer and serial.
The Windows registry is no help here — it only ever reports the generic INF name "HID-compliant mouse" — so the descriptors are read directly with HidD_GetProductString. That means the name you get is often the internal or dongle name rather than the marketing one: an ATK F1 reports as Compx Wireless mouse 8k dongle-L. Rename it to whatever you like. The profile is linked to the device by its VID:PID hardware ID, not by its name, so the name is yours to change.
Once linked, plugging that mouse in selects its profile automatically at startup.
If several devices expose a mouse collection — a keyboard with mouse emulation, for instance — you get a picker instead of a guess. Detection only fills the identity fields; anything you already typed into model, switch type or notes is left alone.
Polling rate measurement
Live test → Measure counts the mouse's actual Raw Input reports for two seconds while you move it, then fills the field with the nearest standard rate and reports the figure it really counted.
It measures what the mouse achieves, which is not always what it is configured for. If you do not move enough, it says so rather than inventing a number, and it refuses to run at all during test mode so it can never sit beside the timing path.
The rate is intervals divided by the time those intervals span. Counting over wall-clock under-reports whenever your hand pauses, and taking the median interval over-reports whenever Windows delivers a batch of reports at once — both were observed while validating this against synthetic input at known rates, where the formula tracked injected rates from 125 Hz to 4 kHz to within 0.1%.
DPI — not detected, and not faked
No Windows API exposes mouse DPI. It lives entirely inside the mouse, and vendor software reads it over undocumented HID reports that differ per manufacturer. The field stays manual, with a tooltip saying why.
A guessed DPI written into a benchmark's metadata would be worse than an empty one, so it is left blank for you to fill in from your mouse's own software.
Also in this release
- Schema v2 adds
devices.hardware_id. Your existing archive is migrated in place — columns are added, nothing is dropped or recreated. - Every Win32 call in the new module declares its
restype/argtypes. Without that ctypes truncates 64-bit handles on x64, and a truncatedGetModuleHandleWresult madeRegisterClassWfault with an access violation — caught before release. - 246 tests, 87% coverage of the non-GUI code, CI on Windows and Linux.
Unchanged
The measurement pipeline is untouched: TRIG↔click association, t₀/t₁, the calibration offset, the 2–100 ms filter, debounce/re-arm, test mode and the OLED freeze all behave exactly as in 1.0.0. Firmware is still v1.4 — no reflash needed.
Full Changelog: v1.0.0...v1.1.0
MouseLatencyTester 1.0.0
First public release.
Microsecond-accurate mouse click latency measurement with a Teensy 2.0 probe, plus a desktop dashboard for running, archiving and comparing benchmarks.
Install
Download LatencyTester-1.0.0-Setup.exe below and run it.
It installs the dashboard, the firmware sketches and the full documentation into a single folder, so you can flash the Teensy without cloning the repository. Per-user install by default, so no admin rights are needed. Your measurement archive lives in Documents\LatencyTester\ and is never removed by the uninstaller.
The installer is not code-signed, so Windows SmartScreen will warn on first run. Verify against SHA256SUMS.txt:
187BD03B7C52F1A7AF9F367FFC6F7562B3C4CE99B6ECA573E8354B17E7616636
From 1.0.0 onward the app can update itself: Settings → Check for updates downloads the next release, verifies it against the published SHA-256, and installs it in one click.
No hardware? Try it anyway
Settings → Demo mode → Start demo device. A simulated Teensy that speaks the real serial protocol drives the whole interface, the charts and the archive. Runs recorded that way are flagged [DEMO] and never mixed with real measurements.
What's in it
- Live test — mean, median, min, max, std dev, P5/P95/P99, IQR, MAD and jitter (P95−P5), with a live chart. Outliers are marked, never removed: the raw sample stays in the database and in every statistic.
- Compare — several runs at once as raw samples, box plot, ECDF or histogram, with a selectable baseline and Δ median / Δ mean / Δ P95 / Δ P99 in ms and percent. Charts export to PNG, SVG or PDF.
- Sessions — searchable archive. Rename, edit metadata, duplicate as a template for the next configuration, export to CSV.
- Devices — profiles per mouse: manufacturer, model, serial, switch type, usual firmware.
- Test mode — full screen, colour-coded: 🟩 press · 🟥 wait · 🟦 done. The OLED is frozen while it runs so the display cannot disturb the timing.
- 8 languages — English, Italiano, Deutsch, Español, Français, 日本語, Русский, 中文.
- Light / dark / system themes. Both preferences persist.
- One-click update, checksum-verified.
217 tests, 87% coverage of the non-GUI code, CI on Windows and Linux.
Firmware
Flash firmware/latency_tester_oled_ldr_v1_4 (included in the install folder). It adds the BTN1/BTN2 test events on pads B0/B1 — polled, never on an interrupt, non-blocking 30 ms debounce, events queued so nothing is written to the serial port between t₀ and t₁. The measurement path is byte-identical to the verified v1.3 baseline.
Older sketches (v1.0–v1.3) are kept and still work; the dashboard treats unknown lines as log output.
Known limitation, documented on purpose
The calibration echo is sent as two bytes where the firmware reads one, which desynchronises the ping loop and collapses the calibration offset towards its +250 µs constant. Every archived measurement was taken with this behaviour, so it has not been changed — fixing it would shift every latency value and break comparability with existing runs. Full explanation in docs/serial_protocol.md.
Not implemented
Button behaviour is not bound yet — v1.4 only reports presses, pending the hardware acceptance test. The 2N2222A transistor and its 220 Ω resistor are physically present but deliberately unconnected: no firmware, no protocol, no invented wiring. There is no photoresistor-based measurement mode; the KY-018 provides the LIGHT telemetry value and nothing else.
Full Changelog: https://github.com/PrimeBuild-pc/MouseLatencyTester/commits/v1.0.0