René Cano
All projects

No. 11

NeuroFocusAI

Webcam attention monitor

My role
Sole author
Period
Mar 2026

The problem

I wanted to measure attention during a study session without extra hardware: using only the webcam, detect whether the person is looking at the screen, blinking too much or closing their eyes, and give them a summary at the end.

What I built

I built it alone in March 2026.

  • Face detection with MediaPipe Face Landmarker: 478 face landmarks per frame.
  • Blink detection with each eye's Eye Aspect Ratio (EAR): blinks per minute and time with the eyes closed.
  • Approximate head orientation, with yaw and pitch computed from the nose, ears, forehead and chin.
  • A rule-based classifier: it starts at 100 points and subtracts for eye fatigue, closed eyes, head out of range (25° of yaw, 20° of pitch) and more than 25 blinks per minute. From the smoothed score it assigns 4 states: high attention (70 or more), medium attention (40 or more), distraction and fatigue.
  • Two views: an OpenCV window with a metrics panel, and a web dashboard that receives the data over WebSocket.
  • A session summary written by Gemini. If the call fails or there's no API key, it falls back to a template summary.

Architecture

Flow diagram: the webcam feeds frames to MediaPipe Face Landmarker, which detects 478 face landmarks; from them come eye signals (EAR and blinks per minute) and head signals (yaw and pitch); a rule-based classifier computes a score out of 100 and one of 4 states, shown in an OpenCV window and in a web dashboard over WebSocket; at the end, Gemini or a template writes the session summary.

Results

There's no measured evaluation. The thresholds are fixed and I haven't validated them with users, so the states are a rule-based approximation, not a measure of attention.

Known limits

  • Orientation uses only yaw and pitch, no roll, computed from the 2D geometry of the landmarks rather than a 3D head model.
  • The README describes things the code doesn't do: a stressed state, 468 landmarks, roll, and instructions to run the dashboard as a separate server on port 5000, when main.py actually starts it. It needs fixing.
  • There are no tests.

Links