Click anything on Windows without touching the mouse. Press a hotkey, every clickable thing gets a letter, type the letter.
┌─────────────────────────────────────────┐
Ctrl+Alt+Space → │ [f] File [g] Edit [h] View │
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ [j] Save │ │ [k] Open │ │
│ └──────────┘ └──────────┘ │
│ │
press j → │ ...clicked Save. No mouse involved. │
└─────────────────────────────────────────┘
macOS has Homerow for this, paid. On Windows, hunt-n-peck and keyhop do similar things. hop is the tiny one: a single file, an 18 KB exe, and arrow-key steering.
Download hop.exe from Releases and run it. No installer, no runtime and no admin rights needed. To build it yourself instead:
git clone https://github.com/BeForce1/hop && cd hop
.\build.ps1
.\hop.exeThen press Ctrl+Alt+Space in any window. A tray icon tells you which hotkey bound: it tries five and takes the first one that's free.
Autostart it:
Set-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run' hop "$PWD\hop.exe"| Key | Does |
|---|---|
Ctrl+Alt+Space |
Label every clickable thing in the focused window |
f g h j k l … |
Type a label to click it |
W A S D / arrows |
Steer the green selection to the nearest target that way |
Enter / Space |
Click the green one |
Backspace |
Un-type a keystroke |
Esc |
Cancel |
Two ways to land on something: type its label if you can see it, or steer if you'd rather look than read. The selected target turns green and gets its whole control outlined, so you can see exactly what you're about to click.
Labels never use w a s d; those steer. That costs 4 of 26 letters, leaving 22
single-key labels and 484 two-key ones.
csc.exe ships inside Windows at %WINDIR%\Microsoft.NET\Framework64\v4.0.30319,
so this compiles on a clean machine with nothing installed: no SDK, no NuGet, no
project file. Output is an 18,944-byte exe that needs no runtime, because .NET
Framework 4.x is part of the OS.
One source file, 445 lines. build.ps1 is 21 lines.
.\hop.exe --dump 3 | Out-Host # focus a window, wait 3s, print every target detectedhwnd 786950 -> 8 clickable in 126ms
f MenuItem [0,0 44x44] System
g TabItem [16,-1 480x64] pwsh
h Button [422,8 64x48] Close Tab
j TabItem [496,-1 480x64] bash
k Button [902,8 64x48] Close Tab
l TabItem [968,-1 496x64] cmd
e SplitButton [1468,7 126x48] New Tab
Useful for filing a bug: run it against the window that misbehaved, paste the output.
The | Out-Host makes PowerShell wait for the output. Without it the prompt comes back first and the list prints over it.
UI Automation first, synthetic mouse last:
Editcontrols getSetFocus(): you want the caret, not a clickInvokePattern: buttons, links, menu itemsTogglePattern: checkboxesExpandCollapsePattern: combo boxes, tree nodesSelectionItemPattern: list items, tabs- Otherwise: move the cursor and click for real
Pattern invocation doesn't move your mouse and works even when the window isn't focused, which is why it's tried first.
The one performance trick that matters: a CacheRequest batches every property
read into a single cross-process call. Without it, a busy window takes seconds;
with it, a live Windows Terminal window enumerates in 126 ms.
The hotkey path has a hard 1.5 s budget and a 484-element cap. Past that, targets are
dropped rather than making you wait. --dump uses a 5 s budget instead, on the grounds
that a diagnostic should show you everything it can find.
Chrome, Edge and Electron build their accessibility tree lazily, and the first UIA query is itself what triggers the build, so it returns browser chrome and no page content. Measured on a cold Chrome showing 30 links:
| pass | elements | links |
|---|---|---|
| 1 | 13 | 0 |
| 2, +250 ms | 33 | 20 |
So on a Chrome_WidgetWin_* window, if nothing landed inside the document's rect, hop
sleeps 250 ms and rescans, keeping whichever pass found more. The trigger is document
emptiness rather than a low count: an already-awake small window measured 19 targets,
so any threshold would tax it forever.
Cost: cold 498 ms, awake 143-159 ms, non-Chromium 131-156 ms (unchanged).
- System: Windows 10 or 11. It needs .NET Framework 4.x, which ships with Windows.
- CPU: 0% while idle. It only works when you press the hotkey.
- Memory: about 23 MB while idle, almost all of it the .NET Framework runtime.
- Disk: one 18 KB
.exe. - Startup: about 55 ms.
Measured on Windows 11 with .NET Framework 4.8.1: 30 s idle sample, 5 launches.
- Homerow (macOS, paid): the thing this imitates. Better polished.
- hunt-n-peck (Windows): the same idea on UI Automation, around for years.
- keyhop (Windows, Rust): hint labels over every clickable thing, home-row alphabet.
- vimium-everywhere (Windows): an AutoHotkey script its own README calls unstable.
- Vimium / Vimium-C: same idea, browsers only, excellent at it.
- PowerToys Mouse Jump teleports the cursor to a screen region. Different problem.
If you want this inside a browser only, use Vimium. It's better at that than hop is.
Stated up front so nobody has to discover them:
- A cold Chromium window pays one 250 ms wake, once. See Waking Chromium. Nothing else is affected.
ControlType.Customis excluded. Electron exposes thousands of them and they bury the real controls. If something has no label, this is usually why.- Scrollbar parts are excluded. UIA reports the trough as a
Button, one 32x1697 "target" that scrolls rather than clicks. Dropped byAutomationId, which is not localised. On a terminal window that was 3 of 11 labels reclaimed. - WASD steers the selection; it does not scroll the page. Only on-screen controls are ever labelled, so anything below the fold is invisible until you scroll there.
- Steering is geometric, not tab-order. Nearest target in the direction pressed, penalising sideways drift 3:1. Good on grids and columns, mediocre on scattered layouts.
- Per-monitor mixed DPI can misplace labels.
SetProcessDPIAware()handles uniform scaling, not per-monitor v2. - Left click and focus only. No right-click, no drag, no scroll.
- Windows running as administrator can't be read (Task Manager, Registry Editor, elevated terminals) unless hop also runs as administrator. Windows blocks UI Automation across that boundary, so the hotkey finds nothing there.
MIT. See LICENSE.