Inspiration

Over the years, I've found myself getting less and less sleep over dumb choices. Cramming for a test the night before would leave me knocked out in my chair, neck folded 90 degrees and all. It would be at these moments where my dad walks in and catches me having a bad time. He would joke with me, saying my bed is a lot more comfortable than having a folded neck in a chair. This evolved into a habit of 30 minute naps laid across 3 office chairs, or even just on the carpet, when I felt like I couldn't keep going. My dad would still tell me to go to my bed, and nap there, but I was never sure of whether or not it was worth it. Last Call is my attempt at fixing this problem, and getting quality sleep whenever possible. I know it can help at least a few people going through the same experience. Also, using it could spare my dad a headache or two ;).

What it does

Last Call runs in a small window while you study in whatever app you use.

  1. Start the night. It asks what you're studying (optional) and calibrates to your face for a few seconds, so "eyes closed" and "head down" are measured against your normal posture.
  2. It watches for real fatigue. A microsleep (eyes shut 0.8 s or more while facing the screen) gets a soft chime and 1 point. Eyes still shut at 10 s, nodding off (eyes close, then the head drops), or 60 s without a blink, glance or head movement are worth 2 points. A yawn adds 0.5. Reading with your head or eyes down doesn't count. Points expire after 10 minutes.
  3. "Nodding off?" At 2 points your alarm plays, the window jumps to the front with keyboard focus, and a 15-second countdown starts. Press F for a false alarm and nothing is sent to Claude or booked.
  4. The agent decides. Claude reads your next few days of calendar events and picks one of three actions, explaining the deadline it reasoned about:
    • Go to bed when nothing urgent is close.
    • Take a 20- or 90-minute nap when something is due soon but there's time. It books the block on your Google Calendar.
    • Quick reset (cold water, a short walk) when the deadline is too close to sleep.
  5. Desk nap. For naps of 20 minutes or less, the camera turns off, a countdown runs, and a louder alarm wakes you at the end of the calendar block, repeating until you press I'm up.
  6. Wake-up quiz. Three multiple-choice questions on what you were studying, built from your topic and the window you were in. Skippable at any time.
  7. Session report. When you quit, a report opens in your browser: total sleep for the night (desk naps plus bed), a timeline with every microsleep, fatigue stats, everything the agent said, quiz scores, and past nights. The next time you open the app it asks when you actually got up, so the bed time switches from the calendar's estimate to the real number.

Every step can be undone: snooze (camera off), false alarm, or Undo, which deletes the calendar block it booked.

Who it's for

Last Call is for students who study late at a laptop, especially in the days before exams and deadlines. It solves a specific problem: you can't judge your own fatigue at 2 AM, and you can't weigh it against your schedule when you're half asleep.

Why someone would actually use it:

  • It needs nothing new. A laptop webcam, the Google Calendar you already keep, and one command to start. No wearable, no app to log into each night.
  • It gives a decision, not a lecture. Sleep trackers tell you the next morning that you slept badly. Last Call tells you tonight, at the moment it matters, what to do about it given what's due.
  • It respects that sometimes you can't sleep. If the exam is in six hours, it won't tell you to get eight. It picks the best option you actually have.
  • It answers the question I ask every morning: how much did I really sleep between the nap at the desk and the hours in bed? The report adds it up for me.

The same pattern fits anyone who works late at a screen on a deadline: shift workers, remote workers, people pulling a late night before a launch.

What makes it different

Drowsiness detection usually exists for drivers, and its whole answer is an alarm: wake up. Sleep trackers come at it from the other end and tell you the next morning how you slept. Last Call sits in between, at the moment of the decision, and asks a different question: given how tired you are and what's due, what's the best use of the next hour?

  • Fatigue meets your schedule. The webcam supplies the "how tired" and the calendar supplies the "what's at stake". Neither alone can decide between bed, a nap and a quick reset.
  • It acts, then closes the loop. It books the nap, wakes you from it, quizzes you on what you were reading, and adds the nap into your night's sleep total.
  • An anti-snooze button. A normal snooze keeps you asleep; this one is for when you're awake and the app got it wrong.
  • Sleep math across fragments. A desk nap at 1 AM plus bed at 3 AM is one night, with the bed time confirmed the next morning instead of guessed.

How we built it

Last Call is a Python desktop app with three layers: local computer vision that never sends video anywhere, a fatigue state machine that decides when to ask, and Claude with tools that decides what to do.

(See the architecture diagram in the image gallery.) Everything on the laptop side runs locally; Claude receives only fatigue counts, calendar events, and the study topic with recent window titles.

Design choices

Every feature came from one of four rules. The user is tired, so the app has to be right, cheap to correct, private, and honest about what it knows.

1. Don't cry wolf. A tool that nags gets closed.

  • Calibration measures your open eyes and your normal head angle, and waits until you finish typing your topic so it doesn't learn your keyboard posture.
  • Reading isn't sleeping: head tilted down or eyes looking down at notes is excused, using gaze direction as well as eyelids.
  • But order matters: if your eyes shut while upright and then your head drops, that's a nod, and it counts even if your face leaves the frame.
  • One microsleep is a chime, not an alarm. The popup needs a pattern.

2. Every mistake is cheap to undo.

  • The 15-second "Nodding off?" window exists so false alarms cost nothing: no Claude call, no calendar event.
  • If the agent already booked a block, Undo deletes that exact event.
  • Snooze is the reverse of an alarm clock's: you press it when you're awake and the app got it wrong (talking to someone, looking away). Each press pauses watching for 5 more minutes, camera off.

3. The camera only runs when it's needed. Video never leaves the laptop; face tracking is local. While you're snoozed, napping, being woken or taking the quiz, the camera is fully off and the window goes black with just the countdown. The quiz reads window titles, not your screen.

4. Code owns the facts, the model owns the judgment.

  • Claude decides how long to sleep; the code computes the exact clock times and writes them into the message, so the calendar and the advice can never disagree.
  • Short naps (20 min or less) happen at the desk with an alarm, because that's where you'll be. Longer rest means bed, so the app closes and you set your own alarm.
  • The report labels bed time "estimated" until you confirm when you got up. Nights run noon to noon so a 2 AM session counts with the 10 PM one before it.

Under the hood

Detection (local, every frame)

  • MediaPipe Face Landmarker (478 landmarks plus blendshapes) in video mode, with OpenCV for capture and drawing. OpenCV handles the camera and the UI; MediaPipe’s face landmark model does the actual eye and head tracking, entirely on-device.
  • Eye aspect ratio per eye, thresholded at 50% of your calibrated open-eye value; a separate "nearly shut" threshold at 30% always counts.
  • Gaze-down blendshapes and a head-pitch ratio (forehead, nose, chin) tell reading apart from sleeping.
  • Nod detection by order of events: a closure that lasts 0.4 s while upright is "committed", so a later head drop or the face leaving the frame keeps counting instead of excusing it.
  • Long microsleeps escalate in place at 10 s, so someone who fully dozes off still trips the alarm without waiting for their eyes to open.
  • Stillness: head landmarks, iris position relative to the eye corners, and blinks, smoothed against tracker jitter. 60 s with none of them is a fatigue event.

Fatigue state machine (main.py)

  • Weighted events in a rolling 10-minute window trigger the popup at 2 points.
  • States: watching, confirm countdown, agent thinking, recommendation, desk nap, waking, quiz, snoozed. The camera is released in every paused state and reopened (with tracking timers reset) afterward.
  • Windows integration through ctypes: forcing focus to the popup (AttachThreadInput + SetForegroundWindow) with a 1-second guard against stray keystrokes, topmost windows, and window-title reads.

The agent (agent.py, Claude Sonnet 5.5 tool-use loop)

  • Tools: get_upcoming_events, create_calendar_event, and give_recommendation for a structured final answer.
  • create_calendar_event takes a duration, not timestamps. The code computes start and end from the system clock and returns them, which fixed a real bug where model-written times landed hours off.
  • One booking per decision, never edits or deletes other events. Undo deletes the booked event through the Calendar API directly, with no model call.

Wake-up quiz (quiz.py)

  • One GetWindowTextW call every 3 seconds keeps the last five useful window titles, with browser and app names stripped and terminals, new tabs and the app itself filtered out.
  • The quiz is generated in a background thread while you nap, so it's ready when you wake. Claude returns it through a tool call; a validator drops malformed questions and accepts lists sent as JSON strings, with a plain-text fallback.

Session report (report.py)

  • Append-only history in one JSON file, written atomically (temp file plus os.replace) behind a lock, because the agent and quiz run on background threads.
  • Nights are grouped noon to noon; sleep = desk naps (start to alarm) + bed time (estimated from the calendar block, confirmed by the morning question).
  • A self-contained HTML page with inline SVG timeline and charts, light and dark themes, no external libraries.

Challenges we ran into

  • Reading looks like sleeping. Looking down at notes lowers your upper eyelids, so the first version flagged reading as eye closure. I added gaze-down blendshapes and head pitch to excuse it, then realized the fix created a hole: people who nod off also look down. Tracking the order (eyes close first, then the head drops) separated the two.
  • Calendar times landed hours off. At first the model wrote ISO timestamps for nap blocks, and they didn't match what it told the user. I moved all clock math into code. A remaining 7-hour shift turned out to be the calendar's own time-zone setting.
  • False alarms cost money. Calling Claude and booking an event the instant fatigue crossed the line meant every false alarm spent API credits and needed cleanup. The 15-second confirm window fixed both.
  • A crash on recalibrate. MediaPipe's video mode rejects timestamps that go backwards, and recalibrating reset the frame clock. Now the clock is set once and only the thresholds reset.
  • The camera gap looked like sleep. Turning the camera off during snoozes meant the first frame back saw "no movement for 5 minutes". Tracking timers now reset whenever the camera reopens.
  • The quiz failed silently. The model I used doesn't support forcing a specific tool, and the error was swallowed in a background thread. I added an on-screen reason for every failure plus a debug log, which found it in one try, then switched to letting Claude choose the tool with a text fallback.

Accomplishments that we're proud of

I'm proud that it works end to end on a real night: it caught me nodding off, read my real calendar, booked a nap, woke me up, quizzed me, and gave me a sleep total I'd otherwise have worked out by hand. It also handled a case I never wrote a rule for: a lecture on my calendar with a recording available later, which the agent correctly treated as skippable.

What we learned

  • Split judgment from facts. The model is good at weighing a midterm against fatigue; code is better at clock math, counting and state. Each bug I hit with the agent got fixed by moving a fact out of the model's hands.
  • Detection is a design problem. Thresholds alone weren't the hard part. Deciding what counts as "asleep" (order of events, gaze, stillness) and how to recover from a wrong guess mattered more.
  • Make failures visible. Silent fallbacks feel safe but hide bugs. One on-screen reason found a problem that guessing hadn't.

What's next for Last Call

  • A phone wake-up for bed sleep: a calendar reminder at the end of the sleep block, so longer rest gets an alarm too.
  • Opt-in screen-aware quizzes: one screenshot of the window you were studying, taken when the popup opens, so questions come from your actual notes instead of the topic.
  • Thresholds that learn you: use past nights to tune what counts as a microsleep for each person.
  • Energy-aware planning: learn when you usually start fading, and schedule hard subjects before that and review after.
  • Mac and Linux support: the detection is cross-platform; the window focus, sounds and title reads are Windows-only today.

Built With

  • anthropic-claude-api
  • google-calendar-api
  • google-oauth
  • html
  • mediapipe
  • numpy
  • opencv
  • plyer
  • python
  • pyttsx3
  • svg
  • win32-api
Share this project:

Updates

Submission history