Hand gesture + eye tracking desktop control — point, pinch, drag, resize, gaze
Project description
GazeControl
Desktop control via hand gestures. Pinch to click, hold to drag, two fingers to scroll — all from a webcam.
Eye tracking (gaze-assisted pointer) is on the roadmap. The code path exists behind an optional extra and a startup mode selector, but it requires per-user calibration and is not production-ready.
Quick start
pip install gazecontrol
gazecontrol
That's it — webcam in, cursor out. Default mode is hand-only; no calibration required.
Hardware requirements
| Item | Minimum |
|---|---|
| Webcam | 720p @ 30 fps |
| OS | Windows 10 / 11 |
| GPU | None required |
Win32-only for now (window manager uses native Windows APIs).
Installation
# Production
pip install gazecontrol
# Development
git clone https://github.com/<you>/gazecontrol
cd gazecontrol
pip install -e ".[dev]"
pre-commit install
Run gazecontrol --doctor to verify the camera and dependencies.
Gestures
| Gesture | Action |
|---|---|
| Index pinch (tap) | Left click |
| Index pinch (held) | Drag the hovered window |
| Index pinch in corner | Resize the hovered window |
| Two fingers up / down | Scroll |
| Double pinch | Toggle the app launcher |
Configuration
Settings load from environment variables (prefix GAZECONTROL_,
double underscore for nested groups) and an optional settings.toml
in the working directory. See settings.toml.example for the full
surface.
# Example: bump pinch threshold
export GAZECONTROL_INTERACTION__PINCH_DOWN_THRESHOLD=0.04
CLI
gazecontrol # run hand-only pipeline
gazecontrol --no-overlay # headless run (no Qt HUD)
gazecontrol --doctor # probe camera + deps
gazecontrol --dump-config # dump effective settings as JSON
gazecontrol --benchmark 30 # run 30 s, print per-stage latency
Roadmap — eye tracking (experimental)
An eye-hand input mode exists behind the [eye] extra. It pairs
the hand pipeline with an L2CS-Net + eyetrax gaze ensemble and a
PointerFusionStage that lets gaze drive target selection while hand
keeps click/drag precision.
Status: works on the maintainer's machine but needs per-user calibration, an unbundled L2CS ONNX model, and tighter drift correction. Not recommended for end users yet. Track progress in docs/architecture.md.
Project layout
src/gazecontrol/
├── cli.py # entry point + doctor + benchmark
├── runtime/ # input mode + pipeline factory
├── pipeline/ # CaptureStage → GestureStage → ...
├── gesture/ # MediaPipe + rule/MLP classifiers
├── interaction/ # InteractionFSM + WindowHitTester
├── filters/ # 1€, Kalman, dead-zone, accel curve
├── overlay/ # PyQt6 HUD
├── window_manager/ # Win32 wrappers
├── gaze/ # roadmap: eye-tracking backends
└── settings.py # pydantic-settings
See docs/architecture.md for the full diagram and ADRs.
License
GazeControl is released under the MIT License — see
LICENSE.
Bundled or downloaded components from third-party projects (L2CS-Net,
MediaPipe Tasks) retain their upstream licenses — see
NOTICE.md for attributions and full text.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gazecontrol-0.8.0.tar.gz.
File metadata
- Download URL: gazecontrol-0.8.0.tar.gz
- Upload date:
- Size: 118.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ea68d48dc8ac26432d1ef4355d3a0392da2275d17b7f1d3883f2c896e110bcb0
|
|
| MD5 |
4f392493d97f712c694b7cdc8d4cdd34
|
|
| BLAKE2b-256 |
b953d52191257ce52ec218c87fc3df159baaeafdeeadfbe7bcb89cc476ed3be1
|
Provenance
The following attestation bundles were made for gazecontrol-0.8.0.tar.gz:
Publisher:
release.yml on TheDarkHoleYT/gazecontrol
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gazecontrol-0.8.0.tar.gz -
Subject digest:
ea68d48dc8ac26432d1ef4355d3a0392da2275d17b7f1d3883f2c896e110bcb0 - Sigstore transparency entry: 1392889551
- Sigstore integration time:
-
Permalink:
TheDarkHoleYT/gazecontrol@cfab1a3edd32674a814a6438f5e7a300cc4e520d -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/TheDarkHoleYT
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@cfab1a3edd32674a814a6438f5e7a300cc4e520d -
Trigger Event:
push
-
Statement type:
File details
Details for the file gazecontrol-0.8.0-py3-none-any.whl.
File metadata
- Download URL: gazecontrol-0.8.0-py3-none-any.whl
- Upload date:
- Size: 141.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7fe74d09f58fe352af02b2dbc5091113a5cbab1d10b83db11795c217843e6e64
|
|
| MD5 |
06e4c0d86376dd55864b39dbaa3d1d40
|
|
| BLAKE2b-256 |
fff11b36d367bb571cbbc8a9c57ebf532cd503911bd09cf92bb4dc1f24c3f903
|
Provenance
The following attestation bundles were made for gazecontrol-0.8.0-py3-none-any.whl:
Publisher:
release.yml on TheDarkHoleYT/gazecontrol
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gazecontrol-0.8.0-py3-none-any.whl -
Subject digest:
7fe74d09f58fe352af02b2dbc5091113a5cbab1d10b83db11795c217843e6e64 - Sigstore transparency entry: 1392889760
- Sigstore integration time:
-
Permalink:
TheDarkHoleYT/gazecontrol@cfab1a3edd32674a814a6438f5e7a300cc4e520d -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/TheDarkHoleYT
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@cfab1a3edd32674a814a6438f5e7a300cc4e520d -
Trigger Event:
push
-
Statement type: