Useful? A star is how other developers find it — ★ GitHub · pixelcoords.dev
Every screen tool that measures pixels ends at a human's eyeball: a ruler shows you a number, a screenshot app draws an arrow, a mouse tracker prints a position you copy by hand. pixelcoords starts from a different premise — the real consumer of a coordinate is a machine.
Freeze the screen so nothing moves while you measure, mark regions with real shapes, and what you marked becomes data, not a picture: versioned JSON in physical pixels with per-monitor DPI scale, labeled crops, and ready-to-paste click code for your automation stack.
Install
| Route | Command | Worth knowing |
|---|---|---|
| Homebrew | brew tap nolindnaidoo/tapbrew install pixelcoords |
macOS only. The formula has no Linux build — the Linux binary needs a capture stack (libxcb, libpipewire, libegl, libgbm) wired to system paths Homebrew does not own. Tap once; after that it installs like any formula. |
| winget | winget install nolindnaidoo.pixelcoords |
Windows 10+. A portable install: winget unpacks the exe and registers a PATH alias, so there is nothing in Add/Remove Programs. |
| cargo | cargo install pixelcoords |
Any platform, needs Rust 1.88+. It compiles the capture stack from source, so expect minutes rather than seconds. On Linux install the build dependencies below first. |
| Prebuilt binary | releases page | macOS (arm64 + x86_64), Windows, Linux. No toolchain needed — download, unpack, run. No auto-update: you come back here for the next version. Each release ships checksums. |
Building on Linux needs the capture stack first:
macOS asks for Screen Recording permission on first run.
Sixty seconds
resolve prints the click point for every label you marked, in the space
and units your automation API speaks:
That is the whole loop: mark once, resolve forever. The session is a directory of JSON and PNGs you can commit.
Commands
| Command | What it does |
|---|---|
pixelcoords |
Freeze every screen and open the marking overlay |
resolve |
Where to click for each label, in your API's space and units |
find |
Re-locate regions in a fresh capture — for when the UI moved |
assert |
Did this point land in the right region? One point or a stream |
diff |
Do the regions still look like they did? |
wait |
Block until regions match again, or until one stops matching |
emit |
Ready-to-paste click code for pyautogui, cliclick, xdotool, and more |
shoot |
Plain scripted screenshot, no overlay, same DPI handling |
resume |
Reopen a saved session and keep editing |
rename |
Give a session a friendly name for the resume picker |
windows |
List visible windows, for --target |
doctor |
Check permissions, config, and the monitor setup |
mcp |
Serve the agent surface over MCP on stdio |
Full reference with every flag: docs/CLI.md
Things worth knowing early
Coordinates are physical pixels, and --units is the knob. A session
records what the display actually has. --units auto converts to what your
platform's input API expects — logical points on macOS, physical pixels on
Windows and X11. Guessing wrong on a Retina display puts every click at
half the intended position.
Exit codes are the API. Scripts branch on them:
| Code | Meaning |
|---|---|
| 0 | yes — resolved, matched, hit |
| 1 | a real answer, and it is no — not found, missed, timed out |
| 2 | the question was malformed — bad label, unreadable session |
Wayland withholds window geometry. windows and --target refuse
there and point you at --pick instead. That refusal is deliberate; a
guessed window position would be worse than no answer.
--min-score and --tolerance are different quantities. The first is
a correlation score in 0..=1; the second is a percentage of pixels in
0..=100. Both are bounds-checked, so a swapped value is refused rather
than silently matching nothing.
Configure it
Colors, key bindings, snapping, and overlay behavior live in a config file you can also override per run:
Every setting and every bindable action: docs/CONFIGURATION.md
Platform support
| Platform | Capture | Window targeting | Verified by hand |
|---|---|---|---|
| macOS | Yes | --target |
overlay through 0.5.1; multi-monitor + mixed-DPI on real hardware |
| Windows 11 | Yes | --target |
through 0.4.0; multi-monitor test-only |
| Linux (X11) | Yes | --target |
through 0.4.0; multi-monitor test-only |
| Linux (Wayland) | Yes, via portal | --pick only |
through 0.4.0; windows/--target refuse by design |
Fractional scaling is unverified everywhere. Claims here match runs — where a run has not happened it says so, and CONTRIBUTING.md carries the full record.
Performance
Measured on an Apple M5 Pro, --release, at 0.5.3. Reproduce with:
| Operation | Size | Median |
|---|---|---|
resolve, all labels |
400 selections | 14.3 µs |
assert, one point |
400 selections | 2.0 µs |
diff, one region |
160×90 crop | 20.6 µs |
find (full-frame NCC) |
160×90 crop | 198 ms |
find (full-frame NCC) |
400×300 crop | 1.40 s |
Resolving is free; matching is the expensive part, and it scales with crop area rather than screen size. Method and caveats: docs/PERFORMANCE.md
Testing
| Layer | What it covers |
|---|---|
| Unit + property tests | pixelcoords-core, 90% line coverage floor per module |
| Contract tests | every exit code, every command — no display needed |
| Scenario tests | the binary driven against a real display, on macOS, Windows and Linux every push |
| Manual gates | the overlay itself — tests cannot speak for it, so it is verified by hand and said so plainly |
659 tests. CI runs fmt, clippy pedantic (-D warnings), the suite, MSRV,
cargo audit, and a policy job that fails on any inline #[allow].
Non-goals
Not a screen recorder, not an OCR tool, not a UI-testing framework, and not an executor — pixelcoords never moves your mouse. It answers where, and stops. pixelactions is the other half of that loop.
Documentation
- pixelcoords.dev — demo, comparisons, how-to
- docs/CLI.md — every command, flag, control, and exit code
- docs/OUTPUT.md — session.json schema, crops, cutouts, jq recipes
- docs/CONFIGURATION.md — colors, key bindings, config file
- docs/TROUBLESHOOTING.md — fixes, behaviors, FAQ
- docs/PERFORMANCE.md — measured timings, and what this tool is not
- docs/DEVELOPMENT.md — building, CI gates, tests, releases
- CHANGELOG.md — what changed and why
Also by nolindnaidoo
Rust
- pixelactions - Perform the interaction and confirm it landed · pixelactions.dev
VS Code Extensions — every tool in the family, one page: letools.dev
- String-LE - Extract string values for i18n from JSON, YAML, CSV, TOML, INI, and .env
- Numbers-LE - Extract numeric values from JSON, YAML, CSV, TOML, INI, and .env
- EnvSync-LE - Spot missing keys across your .env files, with a markdown report
- Paths-LE - Extract file paths from JS/TS imports, JSON, HTML, CSS, TOML, CSV, and .env
- Secrets-LE - Detect and sanitize credentials locally, before you commit
- Scrape-LE - Check whether a page is scrapeable before you write the scraper
- Colors-LE - Extract and analyze colors from CSS, SCSS, LESS, Stylus, HTML, JS/TS, and SVG
- URLs-LE - Extract URLs from documentation, configs, and code
- Regex-LE - Find, test, and validate the regex patterns in the current file
- Dates-LE - Extract and analyze dates from logs, configs, and code
Contact Developer — GitHub · LinkedIn
License
MIT — see LICENSE. Bundled: JetBrains Mono (OFL 1.1).