Inspiration

On our campus, pulling an all-nighter is almost a norm. "I slept three hours" gets a laugh; "I slept eight" gets nothing. Sleep trackers already exist, but they're private, clinical and easy to ignore: a graph you check alone and forget.

We asked what would actually make a college student go to bed. Not another lecture about health, but the same thing that gets people to the gym: friends, competition and streaks. So we built Somnia. It turns sleep into a friendly game with your friends, and it keeps your actual sleep data private.

What it does

Somnia is an iPhone app where friends compete on sleep.

A nightly score from 0 to 100. Every night is scored on four things you can actually control: score = 40 × duration + 30 × bedtime consistency + 20 × quality + 10 × streak. You get full duration marks at 7–9 hours and full consistency marks within 30 minutes of your usual bedtime. Quality is time asleep divided by time in bed. Real sleep data, automatically. Nights sync from Apple Health (Apple Watch) through a one-tap iOS Shortcut, or from WHOOP via OAuth. You can also log a night by hand. Leaderboards and squads. Weekly (resets Monday), all-time, most improved, and squad vs. squad (average member score, so big squads don't win by size). There is deliberately no "worst sleeper" board. Streaks and badges. A streak night is 7+ hours within an hour of your usual bedtime. Badges unlock at 3, 7, 14 and 30 nights (moon, cloud, rainbow, aurora). An AI morning briefing, read aloud. Every morning, Gemini writes a short, personal recap of your night compared only with your own recent nights. ElevenLabs reads it to you with one tap, because nobody wants to read a paragraph with half-open eyes. A weekly AI insight. The app finds your strongest pattern from the last 28 nights, like "You sleep 2 h 33 min more on nights you're in bed by midnight," and Gemini turns it into one friendly tip. Somni, your AI sleep coach (type or talk). Ask "Why was my score so low?" or "How do I catch up to Chris this week?" Somni answers only from your own Somnia data. You can talk to it: ElevenLabs speech-to-text hears your question and Somni answers out loud. Privacy by design. Friends see your score and streak, never your bedtimes or health data unless you choose to share them. You can hide from leaderboards entirely, and you can delete your account and all its data.

How we built it

App: React Native + Expo (SDK 57, Expo Router, TypeScript), running in Expo Go on our iPhones. It has a typed API client generated from the backend's OpenAPI contract, plus mock data for all 8 demo friends, so the UI could be built before the backend existed. We used expo-audio for playback and push-to-talk recording.

Backend: Python 3.12 + FastAPI, deployed on Render next to our database. Pydantic models are the API contract (→ openapi.json → generated TypeScript types). It has signed bearer-token auth with scrypt password hashing, rate limits on login, signup and AI endpoints, and a strict-privacy mode that limits detailed sleep data to its owner.

Database: Tiger Cloud (PostgreSQL + TimescaleDB).

sleep_sessions and daily_scores are hypertables, partitioned by time. A continuous aggregate, weekly_leaderboard, uses time_bucket('1 week') and starts on Monday, matching the weekly reset. It's a real-time aggregate (materialized_only = false), so a fresh sync shows up on the board immediately, with a refresh policy for the rest. A compression policy compresses chunks older than 14 days, segmented by user. We tuned chunk size (90-day chunks) after measuring that small chunks compressed badly, and we wrote a demo script that shows compression at scale: [TODO: ratio from python backend/scripts/compression_demo.py]. We added per-user indexes so overview, history, streaks and rescoring are index reads instead of scans. Getting real sleep data without HealthKit: none of us has a Mac, so we couldn't build a native app with HealthKit. Instead, an iOS Shortcut reads only Sleep Analysis (start, end, stage) from Apple Health and POSTs it as plain text to our backend with a per-user ingest token. The backend merges stage samples into one night per wake date. WHOOP uses a backend-side OAuth flow (the client secret never touches the phone), with HMAC-verified webhooks for new nights.

AI layer (Gemini + ElevenLabs): every AI feature goes through the backend, so API keys never ship in the app.

Gemini (gemini-3.8-flash) uses structured JSON output, with a fallback chain across several Gemini models that skips any model that has hit its daily quota, plus a template fallback so the app never breaks. We compute every number in code and let Gemini only phrase it. The weekly insight, briefing and Somni all get a fact sheet built from the user's own data, so the AI can't invent stats. ElevenLabs text-to-speech (eleven_flash_v2_5) reads briefings and Somni's replies, cached on disk so each text costs one call. ElevenLabs Scribe speech-to-text (scribe_v2) powers push-to-talk Somni. Team workflow: three people on Windows, one role each (data/backend, AI/API client/submission, UI/design). We used small PRs, protected main, additive-only contract changes, and a shared HANDOFF.md updated every work block. The backend has 100+ pytest tests, plus a smoke-test script that runs the whole demo against the live API before we present.

Challenges we ran into

Since neither of Macs or HealthFits and Expo Go can't read Apple Health, we were forced to use mockdata. The iOS Shortcut bridge solved it, but we had to handle time zones carefully: we store device-local wall-clock time so "bed at 11:40 PM" means the same thing everywhere. Free-tier AI quotas at 3 AM. We hit Gemini's daily free-tier limit mid-build. We added a model fallback chain that skips exhausted models, per-night caching, rate limits, and template fallbacks, so the app degrades gracefully instead of breaking. Keeping the AI honest. LLMs love to invent numbers. Our fix was architectural: the code finds the pattern and computes the stats, and Gemini only writes the sentence. Somni gets a data sheet and strict scope rules, and it refuses off-topic, medical and "ignore your rules" requests. Voice in Expo Go. Real-time voice agents need native modules Expo Go doesn't have, and an always-listening wake word isn't possible there. We designed push-to-talk instead: record with expo-audio, transcribe with ElevenLabs, answer, speak back.

Accomplishments that we're proud of

This was the first hackathon for everyone on our team and we were extremely proud of the fact that we were able to create an impactful product under heavy pressure and a hard deadline. It was an incredibly rewarding experience and we were able to learn a lot through this hackathon.

What we learned

Grounding beats prompting. The biggest jump in AI quality came from giving Gemini computed facts, not from better wording. Design for failure early. Quotas, cold starts and flaky Wi-Fi are guaranteed at a hackathon demo, so every AI call has a fallback. TimescaleDB's continuous aggregates are a natural fit for leaderboards. Compression needs enough rows per chunk to pay off, so measure before you tune. An API contract generated from one source of truth (Pydantic → OpenAPI → TypeScript) let three people build in parallel without breaking each other.

What's next for Somni

Native HealthKit and background sync once we can build with Xcode. Wind-down reminders at your bedtime minus 30 minutes, and a spoken wind-down from Somni so you can put the phone face down. A healthier average for sleep scores across college campuses for students, ultimatley leading to better mental health and well-being.

Credit

We used Anthropic's Claude Code to check and improve a lot of our code. We also used it to find more elegant solutions and see if a certain process could be done more efficiently. While all the major architectural decisions were made by our team, we used Claude Code to streamline our process.

Built With

Share this project:

Updates

Submission history