CapsLockX · Browser Test

CLX OFF
WASM loading…
Failed to load the WASM module. Build it first, then serve via HTTP:
wasm-pack build --target web rs/adapters/browser
npx serve rs/adapters/browser # or: python -m http.server -d rs/adapters/browser
Then open http://localhost:3000/www/ (or the port your server uses).

Hotkey Reference

HJKL← ↓ ↑ → (accel)
Y / OHome / End
U / IPage Up / Down
GEnter
TDelete
N / PFocus next / prev
WASDMove virtual cursor
R / FScroll up / down at cursor
E / QLeft / Right click (visual only)
F5Play / Pause
F6 / F7Prev / Next track
F8Stop
F9 / F10Volume + / −
F11Mute
CapsLockHold to activate
SpaceHold to activate
CapsLock+Space Lock CLX (tap either to unlock)
The virtual cursor appears when you first use WASD. Moving the real mouse snaps it back.

Cursor / Edit Test · hold CapsLock then use HJKL

The key log below shows only events CLX passed through — CLX hotkeys won't appear there.

Key Log — events that reached the page (CLX-intercepted keys are absent)

Scroll box · CLX + R / F

Move the virtual cursor over this box (WASD), then press CLX+R/F to scroll it. R/F always scroll the scrollable element under the virtual cursor.

How it works

Hook: capture-phase keydown/keyup listeners on window. Intercepted events get preventDefault() + stopPropagation().

Cursor (HJKL): synthetic ArrowLeft/Right/Up/Down dispatched on the focused element. isTrusted=false but browsers still move the text cursor.

Virtual mouse (WASD): WASM dispatches clx:mouse_move [dx,dy]; JS renders a cursor overlay and moves it. Real mousemove snaps it back.

Scroll (R/F): WASM dispatches clx:scroll_v/h; JS walks up from elementFromPoint(vx, vy) to find the nearest scrollable ancestor and calls scrollBy on it.

Focus (N/P): WASM dispatches clx:focus ±1; JS cycles through querySelectorAll of all tabbable elements.

Focus test · CLX + N (next) / P (prev)

Virtual cursor: hold CLX + WASD to move the crosshair cursor. Move it over the scroll box above, then CLX+F to scroll that box, not the page. Real mouse movement instantly snaps the virtual cursor back.