AccessKit

A PDF reader and a music controller driven by ten facial gestures and a webcam.

Year
2025
Kind
Two prototypes
Stack
Python, OpenCV, MediaPipe, dlib, PyMuPDF, Tkinter
Needs
A webcam; the music controller also macOS and Spotify

Background

For someone who cannot use a mouse or a keyboard, the face often keeps its range of movement. AccessKit asks how far a webcam alone can go as the input device.

Method

Head yaw and pitch come from OpenCV’s solvePnP on six facial landmarks. Blinks and winks come from each eye’s aspect ratio, mouth open and smile from ratios of the mouth’s landmarks, and a raised brow from its height above the eye. A state machine asks for most gestures to be held, for 0.5 to 1.5 seconds in the reader and 2.0 to 5.0 in the music controller, and waits out a cooldown between actions. Every mapping and threshold can be changed in a settings panel and is saved between sessions.

The reader finds the face with MediaPipe’s mesh of 478 landmarks and renders pages with PyMuPDF. The music controller uses dlib’s 68 landmarks, confirms a blink over three consecutive frames, drives Spotify through AppleScript, and also accepts a two-gesture sequence: one gesture held, then a second.

The ten gestures, how each is detected, and what each does by default. Every row can be remapped.
GestureDetected byPDF readerMusic controller
Look rightHead turned right past 30° (reader) or 45° (music)Next pageVolume up
Look leftHead turned left past 30° or 45°Previous pagePlay or pause
Look upHead tilted up past 20° or 25°Scroll upUnassigned
Look downHead tilted down past 20° or 25°Scroll downUnassigned
SmileMouth corners lifted past a set thresholdZoom inHeld, then look right: volume up
Open mouthMouth aspect ratio above a set thresholdReset zoomHeld, then look left: previous song
Raise eyebrowsBrow height over eye width (reader); brow height above the eyes, in pixels (music)Zoom outUnassigned
Long blinkBoth eyes closed and heldFull screen on or offNext song
Right winkRight eye closed, left openUnassignedUnassigned
Left winkLeft eye closed, right openUnassignedUnassigned
BC1152332636129161291abc3316015813315314452e159vh1314s20°up20°down115233, 26361, 29130°30°ACanonical face, frontmodel unitsBEye4 : 1CMouth2 : 1DPose, sidepitchEPose, planyaw
  1. From the PDF reader’s code (AccessibilityPDF.py), on MediaPipe’s canonical face model (Apache-2.0), whose 468 points are the first 468 of the 478 the reader’s mesh gives, in the same order; the ten iris points are not in the model.
  2. Head pose: solvePnP fits the six landmarks named in A to the code’s six model points, divided by 4.5, drawn in D and E. Turned past 30° of yaw or 20° of pitch about the nose tip, the head is looking aside, up or down.
  3. Eyes: (a + b) / 2c on each eye, the code’s (A + B) / 2C; B draws the eye with 33, and the other mirrors it. Below 0.25 on both is a blink, held 1.5 s a long blink; on one eye, a wink.
  4. Brows: raised when the mean of the two brows’ heights e, over the eye’s width c, is above 0.85.
  5. Mouth: open when v / h is above 0.6; a smile when the corners’ lift above the centre line s, over h, is above 0.02.
  6. The music controller uses dlib’s 68 points; its thresholds are in the table.

Results

The table is the result: ten gestures, each found in the code with the rule shown and each remappable. No accuracy, latency or frame rate is given, because none was measured.

Limitations

Neither app has been tried by people with motor impairments, the people it was built for, so whether the gestures are comfortable or reliable for them is unknown. The remappable thresholds are the means of adapting it, not evidence that it adapts.

The defaults were tuned on one face, so they are a starting point that each user would need to adjust. The two apps use different landmark models and share no code, and the music controller measures a raised brow in pixels, so its threshold shifts with distance from the camera; the reader’s ratio does not.

Two public repositories, AccessibilityPDF and AccessibilityTune, 2025.