A hand-gesture-driven 3D CAD prototyping tool that runs in your webcam feed.
Air-CAD lets you select, move, and manipulate 3D wireframe objects in real time using just your hands — no mouse, no touchscreen, no controller. Powered by MediaPipe hand tracking and OpenCV rendering.
| Gesture | Action |
|---|---|
| ✌️ Pinch (thumb + index close together) | Select & drag objects |
| ✋ Open palm | Release / idle |
c key |
Spawn a new cube |
s key |
Export scene to .obj file |
x key |
Clear the scene |
q key |
Quit |
The pipeline has five main stages:
Webcam → Perception → Gesture Engine → Intent Manager → Visualizer
↓
Scene Graph (3D Objects)
- Perception (
perception.py) — Captures each frame and runs MediaPipe'sHandLandmarkerto extract 21 3D hand landmarks per frame. - Gesture Engine (
gestures.py) — Analyses landmark positions to classify the current gesture (PINCH,OPEN, orNONE) using Euclidean distance between thumb tip and index tip. - Intent Manager (
intent.py) — A state machine (IDLE → SELECTING → DRAGGING) that maps gestures + hand position to meaningful actions like selecting or dragging an object. - Geometry (
geometry.py) — DefinesPrimitivebase class,CubeandCylindersubclasses, and aSceneGraphthat holds all objects. Each object stores position, rotation, and scale, and can produce its own model matrix. - Visualizer (
visualization.py) — Projects 3D wireframes onto the 2D camera frame using a perspective projection matrix. Renders object edges, selection highlights, and a status HUD.
Export (export.py) can dump the entire scene to a .obj file using line primitives.
air-cad/
├── main.py # Entry point — main loop
├── perception.py # MediaPipe hand tracking
├── gestures.py # Gesture classification
├── intent.py # State machine & object interaction
├── geometry.py # 3D primitives & scene graph
├── visualization.py # 3D→2D projection & OpenCV rendering
├── export.py # OBJ file exporter
├── utils.py # Math helpers (projection matrix, distance)
├── hand_landmarker.task # MediaPipe model file (required)
└── requirements.txt # Python dependencies
- Python 3.9+
- A webcam
# 1. Clone / download the project
git clone <your-repo-url>
cd air-cad
# 2. Install dependencies
pip install -r requirements.txt
# 3. Make sure hand_landmarker.task is in the project root
# Download from: https://ai.google.dev/edge/mediapipe/solutions/vision/hand_landmarker
# 4. Run
python main.pyopencv-python
mediapipe
numpy
Once running, a window opens showing your webcam feed with a wireframe cube rendered in the scene.
- Pinch your thumb and index finger together near an object to grab it
- Move your hand while pinching to drag it around
- Open your hand to release
- Press
cto spawn additional cubes at random positions - Press
sto save the current scene asair_cad_output.obj - Press
xto wipe the scene - Press
qto quit
The HUD in the top bar shows live FPS, detected gesture, and current interaction state.
Objects live in a right-handed 3D world space. The camera is placed at z = -5 looking toward the origin. Screen drag deltas are mapped to world-space movement with a 0.01 scale factor (Y-axis is flipped to match screen conventions).
Object selection uses a simple screen-space proximity check — the pinch center is compared against the projected 2D position of each object's origin. Closest object within 50px wins. This is a heuristic; true raycasting would unproject the screen point.
The Primitive base class supports full TRS transforms (Translation × Rotation × Scale). Rotation and non-uniform scaling are not yet exposed via gestures in this prototype — they're ready to be wired up.
| Feature | Where to add it |
|---|---|
| New gesture (e.g. two-finger rotate) | gestures.py → detect_gesture() |
| New primitive (e.g. sphere) | geometry.py → subclass Primitive |
| Rotate / scale via gesture | intent.py → new states + geometry.py transforms |
| Better hit testing | intent.py → _raycast() |
| Draw hand skeleton overlay | perception.py → draw_landmarks() |
| Export to other formats (STL, glTF) | export.py |
- Single hand only (
num_hands=1) - Translation only — no gesture-driven rotation or scale yet
- Screen-to-world mapping is a linear approximation (no true unprojection)
- OBJ export uses line primitives (
l), not face-based meshes - No undo / redo
MIT — do whatever you want with it.