How to get pixel coordinates on macOS, Windows, and Linux
Every OS has a built-in way to read a coordinate — they're below, and for a one-off they're all you need. The part that bites is that modern screens have two coordinate systems: physical pixels (the screenshot's own grid) and logical points (what many click APIs use). On a 2× Retina display, the pixel at (1624, 880) in a screenshot is the point (812, 440) to a click API — mix them up and every click lands in the wrong quadrant. Whatever tool you use, know which space its number lives in.
macOS
Built in: press Cmd+Shift+4. The screenshot crosshair follows your cursor with live coordinates (logical points); press Esc to cancel without taking a screenshot. That's the fastest one-off answer on a Mac.
The pitfall: screenshots on Retina are captured in physical pixels at 2× the crosshair numbers, so a coordinate read from a screenshot in an editor won't match. For coordinates that survive — saved, labeled, converted per display — freeze the screen instead:
pixelcoords # freeze, mark the spot, S saves
pixelcoords emit --session <dir> --format cliclick # logical points
pixelcoords emit --session <dir> --format pyautogui # logical on macOSmacOS asks for Screen Recording permission on first run; the session records each monitor's scale so conversions are per-display, correct on mixed-DPI setups.
Windows
Built in: PowerToys' Mouse Utilities and Screen Ruler give quick on-screen numbers — genuinely good for a fast read (full comparison).
The pitfall: per-monitor DPI scaling. A 150% display makes physical and logical disagree by half again, and every monitor can differ. pixelcoords records each monitor's true scale factor in session.json (it declares per-monitor DPI awareness before capturing, so the number is exact, not virtualized):
pixelcoords
pixelcoords emit --session <dir> --format pyautogui # physical px on WindowsHonesty note from the tool's own docs: the pyautogui-on-Windows convention follows its documented DPI-aware behavior and has not yet been verified on hardware.
Linux
Built in on X11: xdotool getmouselocation prints the live cursor position in physical pixels — the classic answer, still the right one for a one-off.
The pitfall is Wayland: its security design withholds global coordinates and window geometry from applications, so live trackers can't exist there. A frozen-snapshot overlay is the model Wayland permits — pixelcoords freezes through the desktop portal, and --pick marks a single window with window-relative coordinates:
pixelcoords # X11: everything works, incl. --target
pixelcoords --pick # Wayland: mark one picked window
pixelcoords emit --session <dir> --format xdotool # physical pxWhen a number isn't enough
A coordinate you read once answers today's question. If the same spot matters tomorrow — in a script, a test, an agent — you want it saved with its context: which monitor, what scale, what the region looked like. That's the pixelcoords session: mark once, then assert verifies points with exit codes, and find re-locates regions after the UI drifts. If the thing reading the coordinate is a model rather than a script, pixelcoords mcp hands it the same answers over the Model Context Protocol. The front page shows the whole loop in sixty seconds.
Questions people actually ask
Why don’t my screenshot coordinates match my click coordinates on a Retina display?
HiDPI screens have two coordinate systems: physical pixels (the screenshot’s grid) and logical points (what most click APIs use on macOS). On a 2× Retina display a screenshot pixel at (1624, 880) is the logical point (812, 440). Divide by the display’s scale factor — or use a tool that records the scale per monitor and converts for you.
How do I get the pixel coordinates of my mouse on macOS?
Press Cmd+Shift+4 — the screenshot crosshair shows live cursor coordinates in logical points; press Esc to cancel without capturing. For coordinates you can save, verify, and hand to a script, freeze the screen with pixelcoords and mark the spot.
How do I get screen pixel coordinates on Wayland?
Wayland’s security design withholds global coordinates and window geometry from applications, so live trackers can’t work. A frozen-snapshot overlay is the model Wayland permits: pixelcoords freezes via the desktop portal, and --pick marks a single window with window-relative coordinates.
How do I get coordinates in the right format for pyautogui?
pyautogui speaks logical points on macOS and physical pixels on Windows and X11. pixelcoords emit --format pyautogui applies the right convention per monitor from the recorded DPI scale. The Windows pyautogui convention follows its documented behavior and has not yet been verified on hardware.
How can a script verify a point is inside a screen region?
Mark the region once, then run pixelcoords assert --session <dir> --point X,Y --expect name. The exit code is the API: 0 hit, 1 miss, 2 malformed question — CI and computer-use agents can gate on it. To score a whole run, --stdin reads one point per line and answers in a single process.
How do I get the coordinate my automation tool actually wants?
pixelcoords resolve --label name --units auto returns the click point in the space your input API expects: logical points on macOS, physical pixels on Windows and X11. Each region converts through its own monitor’s DPI scale, so a mixed-DPI desktop comes out right without the caller knowing it was mixed.
Can a script wait for a dialog to appear before clicking?
pixelcoords wait --label dialog --for match blocks until that region matches its saved crop again, and --for change blocks until one stops matching. It exits 0 when the condition holds and 1 on timeout, so a script can tell "it never appeared" from "the question was malformed". Two honest limits: matching is contrast-normalized, so --for change cannot see a region that dims uniformly behind a modal, and a blinking cursor inside a watched region is enough to trip it on its own — lower --min-score when that happens.
Can an LLM or AI agent use it directly, without shelling out?
pixelcoords mcp serves the same commands over the Model Context Protocol on stdio — point an MCP client at the binary and the model gets six read-only tools: list sessions, resolve a click point, assert a point, wait, find, diff. Asking a marked session where to click reads a file and sends no image at all, where the alternative is a screenshot per step. It cannot mark regions — a human does that once in the overlay, and the model asks about it from then on. A miss or a timeout comes back as an ordinary result with ok false, not as an error, so the model reacts to it instead of retrying.
Can I use it for visual regression testing?
pixelcoords diff compares each marked region against the screen now, or against stored screenshots with --against, and reports how many of that region’s pixels changed. It is scoped to the regions you marked rather than whole screenshots, so a clock in the corner is not a failure, and shaped selections compare by their true silhouette. --tolerance defaults to exact: anti-aliasing usually means CI wants a small nonzero value, but a diff tool that rounds by default is lying by default.
Two ways in
cargo install pixelcoordsOr skip the toolchain: prebuilt binaries for macOS, Windows, and Linux — download, unpack, run.
Rust 1.88+ for the cargo route. On Linux, build dependencies first:
sudo apt-get install -y libxcb1-dev libxcb-randr0-dev libpipewire-0.3-dev \
libclang-dev libegl1-mesa-dev libgbm-dev pkg-configmacOS asks for Screen Recording permission on first run.