🎮 Play Live: https://blinkorpinch.netlify.app
BLINK is a browser-based, computer-vision-powered horror/puzzle game where your physical movements control the player. You must navigate a dark, procedurally generated maze and escape before the "Watcher" catches you. But there's a catch: every time you move, the Watcher moves closer. If you hesitate too long, the Watcher advances anyway.
(https://www.loom.com/share/7b0dec8506f94a6d98a184f74d82389f)
- Face Tracking (Blink Mode): Steer your character by turning your head (yaw/pitch) and move by physically blinking your eyes.
- Hand Tracking (Swipe Mode): Steer by hovering your hand in different quadrants of the screen, and commit your move by pinching your index finger and thumb together.
- Fallback Controls: Standard keyboard fallback (Arrow keys + Spacebar) if a webcam is unavailable.
- Dynamic Composure System: As the Watcher gets closer, your "composure" drops, resulting in a darkening vignette effect and faster penalty timers.
- Procedural Mazes: Infinite replayability with custom Depth-First Search maze generation, including a "braiding" pass to create loops for kiting the Watcher.
- Web Audio API: Fully synthesized, zero-dependency audio cues for a tense atmosphere.
This game runs entirely client-side in the browser.
- Clone the repository:
git clone https://github.com/icemberg/BLINK.git - Serve the project folder using any local web server (e.g.,
python -m http.server,npx serve, or VSCode Live Server). - Open
index.htmlin a modern browser. - Grant camera permissions when prompted.
BLINK is a pure Vanilla JS/HTML/CSS application with zero build steps, making it incredibly simple to deploy.
- Click the Deploy to Netlify button at the top of this README.
- Or, connect your GitHub repository directly in the Netlify dashboard.
- The included
netlify.tomlautomatically configures the root directory.as the publish directory and applies security headers. No build command is required.
BLINK uses a decoupled architecture centering around a reactive GameState model. Inputs (computer vision or keyboard) dispatch events to the GameState, which then notifies the UI (HUD) and Renderer to update.
graph TD
%% Input Layer
subgraph Input Layer
FT[FaceTracker] --> BD[BlinkDetector]
FT --> HY[HeadYaw]
HT[HandTracker] --> FC[FingerControl]
FI[FallbackInput]
end
%% State Layer
subgraph State Layer
GS[GameState]
MG[Maze Generator]
PF[Pathfinding]
Player[Player Logic]
Watcher[Watcher Logic]
end
%% Presentation Layer
subgraph Presentation Layer
R[Renderer Canvas]
H[HUD DOM]
SM[SoundManager AudioContext]
end
%% Connections
BD -- Dispatch 'blink' --> GS
HY -- Dispatch 'yawChanged' --> GS
FC -- Dispatch 'commitMove'/'yawChanged' --> GS
FI -- Dispatch Events --> GS
GS <--> MG
GS <--> PF
GS <--> Player
GS <--> Watcher
GS -- State Changes --> R
GS -- State Changes --> H
GS -- Play Cues --> SM
src/state/gameState.js: The central brain of the game. It holds the maze grid, entity positions, scoring, and current phase. Exposes adispatch(event)method for state transitions.src/main.js: The orchestrator. It wires the vision trackers to theGameStateand runs therequestAnimationFramerender loop.src/tracking/: Contains the MediaPipe integration logic.faceTracker.js&handTracker.js: Load the WebAssembly vision models.blinkDetector.js: Calculates Eye Aspect Ratio (EAR) and debounces noise to detect true blinks.fingerControl.js: Calculates a 3D, scale-invariant ratio between the index and thumb tips to reliably detect pinch gestures regardless of hand distance from the camera.
src/render/: Handles all visual output.renderer.js: Draws the maze grid, player, watcher, and vignette using HTML5<canvas>.hud.js: Manages HTML overlays, the composure bar, and results screens.
HandTrackergets the latest frame and returns 21 3D hand landmarks.main.jspasses the landmarks toPinchCommitControl(fingerControl.js).PinchCommitControlcalculates the thumb-to-index distance normalized by the palm width. If the ratio drops below0.30for 2 consecutive frames, it registers a pinch.main.jsreceives the pinch state and callsgameState.dispatch({ type: "commitMove" }).GameStateupdates the player position, computes the Watcher's A* / BFS pathfinding to chase the player, and recalculates composure.GameStatenotifies subscribers.Rendererredraws the canvas with the new entity positions.HUDflashes the directional prompt.SoundManagerplays a "tick" sound.