macOS · Webcam · One hand

Your hand is the mouse. Pinch to click, hold to drag — the webcam does the rest.

Tactyle Touch watches one hand through the built-in camera and turns pinches and finger poses into real clicks, drags and scrolls anywhere on your Mac — moving the actual system cursor through Quartz, not just inside one window. No touchscreen, no glove, no extra hardware. It lives in the menu bar, and it's built and signed on your own machine, so there's nothing to notarize and nothing to wait for.

Tracking one hand
move click drag scroll right-click switch space dictate stop tracking open

Open hand Move your open hand and the cursor follows the palm.

  • Open hand → move the cursor
  • Pinch & release (<250 ms) → click
  • Pinch, hold & move → drag
  • Two fingers, glide → scroll, with momentum
  • Middle-finger-to-thumb pinch → right-click
  • Three-finger swipe → switch Space (Ctrl+←/→)
  • Thumbs-up, hold → start Dictation
  • ILY 🤟, hold → stop tracking

A closed fist is a deliberate no-op — it also blocks a false pinch, so folding your hand never fires a click.

The honest problem

A webcam sees your hand shake. A mouse never does.

Turning a hand seen through a laptop camera into a usable pointer isn't a UI problem, it's a signal problem. Landmarks jitter frame to frame; smooth them too hard and the cursor lags your hand; and the thing you're aiming at — a 12-pixel checkbox, a traffic-light close button — is smaller than the noise. Most hand-tracking demos look magical for thirty seconds and then can't reliably click anything real.

Jitter

Raw landmarks wobble every frame. Map them 1:1 to pixels and the cursor never sits still long enough to click.

Latency

Kill the jitter with a heavy filter and the cursor drags behind your hand. Steady and responsive is the whole trick.

Small targets

The controls you want to hit are tiny. Without help, a free-hand cursor simply can't land on them — which is exactly what Precision Assist fixes.

From pixels to pointer

A webcam, a hand model, and the same cursor everything else uses.

Nothing simulated and nothing sandboxed to one window: Tactyle Touch reads the camera, finds the hand, decides what pose it's in, and injects the resulting click or scroll as a real system event — the same CGEvent path a trackpad drives.

1

See the hand

MediaPipe Hand Landmarker returns 21 landmarks per frame at ~15–30 Hz. A One Euro filter smooths the jitter without adding lag you can feel.

2

Read the pose

A pure gesture state machine turns landmark geometry — pinch ratios, which fingers are extended — into eight distinct, debounced gestures. No ML in this layer, so it's fully unit-tested.

3

Move the Mac

The menu-bar app maps each gesture to a Quartz CGEvent — move, click, drag, scroll, Ctrl+arrow, Dictation — so it works system-wide, in every app.

gesture state machine
# the pinch that decides click vs drag — frozen in docs/protocol.md pinch_ratio = dist(thumb_tip, index_tip) / knuckle_span armed when ratio < 0.30 for 2 frames # latched, no jiggle click if released < 250 ms and moved < 2% # else → drag # everything else — spaces, dictate, right-click — layered on additively
0 gestures
One hand, eight poses — move, click, drag, scroll, right-click, Spaces, Dictation, stop
0 landmarks
Tracked per frame by the on-device MediaPipe hand model — nothing leaves your Mac
0 tests
Cover the gesture state machine and mapper — the pure logic runs with no camera or display
Precision Assist

A hand in the air can't hit a 12-pixel checkbox. So it doesn't have to.

Free-hand tracking is smooth but never pixel-perfect — the honest problem with every camera mouse. Precision Assist asks macOS's Accessibility API which elements on screen are actually clickable, then gently snaps the cursor to the nearest one as you approach. You aim at the button; it lands on the button.

Raw
jitters past
Assisted
snaps on

Raw cursor drifts around a small target; assisted cursor is pulled onto it once it's close.

The targets aren't guessed. ax_targets.py walks the focused app's Accessibility tree for real clickable elements — buttons, checkboxes, links — and magnet.py applies a distance-weighted pull toward the closest one, strong up close and nothing at a distance, so normal movement never feels sticky.

It's a magnet for things you can actually click — not a grid, not a guess. Toggle it from the menu bar; it's off until you want it.
What's in the menu bar

Small, honest, and yours to inspect.

One status item, a camera preview when you want it, and source you can read end to end.

Live camera preview

An optional menu-bar thumbnail shows the tracked hand skeleton, the pinch pair, and the active region — with mirroring and horizontal-invert toggles.

Momentum scrolling

Two-finger glides scroll with inertia that decays like a trackpad flick — the engine hands off a velocity and the app lets it coast.

Precision Assist

Magnetic snapping to real clickable elements via the Accessibility API. Off by default; one click in the menu to turn on.

Tunable, from env or JSON

Every threshold is a TOUCH_* knob — pinch hysteresis, debounce frames, scroll gain — read from settings.json or the environment.

Browser SDK too

A zero-dependency ES module renders a smoothed cursor and dispatches real pointer events on any web page — the same engine, no Mac permissions.

Private by construction

The hand model runs on-device; frames never leave your Mac. No account, no telemetry, no network at runtime.

Get it

One command, from a checkout.

The installer builds a self-contained TactyleTouch.app (embedded CPython), signs it, copies it to /Applications, and launches it.

$ git clone https://github.com/gokulmc/tactyle-touch.git && cd tactyle-touch && ./install.sh
No notarization needed — it's built and signed on your machine.
Because install.sh compiles and codesigns the app locally with your own certificate, Gatekeeper is satisfied without an Apple Developer round-trip. Build-from-source is the whole install path, not a fallback.
Apple Silicon macOS 13+ 426 MB · 7,834 files ~8 MB model download Accessibility + Camera Apache-2.0

First build takes a few minutes and downloads CPython (cached afterwards); the first camera run fetches the ~8 MB MediaPipe hand model to ~/.cache/tactyle-touch/. The finished .app weighs about 426 MB across 7,834 files — that's a whole embedded CPython plus the vision runtime, bundled so it never touches your system Python. Grant Accessibility (to move the cursor) and Camera when macOS asks — the two permissions it genuinely needs, and nothing more.