Turn pages with a wink. Wink your right eye for the next page, left eye for the previous one. Ordinary two-eye blinks are ignored.
Built for reading PDFs in a browser without reaching for the keyboard. It sends
real key presses (Page Down / Page Up), so it works in anything focused —
Chrome's PDF viewer, Preview, a Kindle web reader, slides.
macOS only, for now.
git clone https://github.com/kuldeeepy/winkscroll.git
cd winkscroll
./setup.shThen grant two permissions in System Settings → Privacy & Security:
| Permission | Why | Prompted? |
|---|---|---|
| Camera | to see your face | yes, on first run |
| Accessibility | to send key presses | no — add it by hand |
Add your terminal app (Terminal, iTerm) to both, then quit and reopen it. macOS won't apply either grant to an already-running process.
Accessibility is the one that catches people out: without it the camera works, the detection works, and nothing happens. It fails silently.
.venv/bin/python menubar.py # menu-bar toggle — 👁 on, 😴 off
.venv/bin/python winkscroll.py # terminalThe menu bar has a start/stop toggle, three sensitivity presets, and a swap for when the eyes come out backwards. Stopping releases the camera, so the green light goes off.
.venv/bin/python winkscroll.py --debugprints live numbers:
R 0.02 L 0.03 gap 0.01 [base 0.05/0.15] 30fps
R/L are how far each eye has closed beyond its own resting point, and gap
is the difference. A wink fires when gap clears the threshold. At rest both
should read near 0.00 regardless of where you're looking.
- Pages turning while you just read →
-s strict - Winks not registering →
-s relaxed - Wrong eye →
--swap
MediaPipe's face landmarker gives per-frame eyeBlinkLeft / eyeBlinkRight
blendshape scores. Three things make it usable:
Per-eye baselines. Most people's eyes don't read the same at rest — mine sit
at R 0.05 / L 0.15. Comparing raw scores makes one eye win every time, so the
detector learns each eye's resting floor and measures the rise above it. It
follows the floor down quickly and up slowly, so a wink can't drag its own
baseline along.
Relative, not absolute. A wink is "one eye much more closed than the other." Looking down at a book raises both scores together, so the gap stays small and nothing fires — which fixed pages turning while reading.
A refractory period. After firing, the eyes must stay level for 0.3s before another wink counts. A single quiet frame mid-reopen is the tail of the wink you just fired on, not a new gesture; arming there turns two pages from one wink.
- mediapipe 1.x crashes on macOS ARM (it grabs a Metal service that isn't
there). Pinned to
<1.0. - Two pages at a time? Check whether your PDF viewer is in two-page/spread
view.
Page Downmoves one viewport, and a spread is two pages. Chrome: ⋮ menu → uncheck Two-page view. - Nothing happens at all? Accessibility permission. See above.
MIT