Inspiration
Hair-progress tracking is noisy: lighting, angles, and inconsistent selfies make it hard to tell whether a routine (minoxidil, finasteride, or grooming) is working. We wanted a tiny, repeatable loop that makes photos comparable, preserves user privacy, and gives clear, non-diagnostic feedback people can act on.
What it does
- Guides a consistent crown/scalp photo capture (fixed oval guide + countdown) so images are comparable.
- Runs on-device object detection (ONNX Runtime Web + YOLOv8n) to find visible hair/scalp concerns.
- Applies deterministic image-quality checks and a deterministic 0–100 visible-health score derived from model detections.
- Enforces a fixed safety review (symptom/quality flags) that the coach cannot alter.
- Produces an educational coach summary (optional LLM provider with a validated mock fallback).
- Persists profiles, check-ins, coach output, and metadata to Neon Postgres (Neon Auth + Drizzle ORM).
- Provides dashboard, history, and progress views that stay empty until a real, authenticated check-in exists.
How we built it
- Frontend: Next.js App Router + React 19 + Tailwind CSS (motion + responsive mobile-first UI).
- Vision: ONNX Runtime Web in-browser inference with YOLOv8n, letterbox preprocessing, and class-aware NMS.
- Validation & services: Zod-validated analyze API, deterministic scoring and safety services written in TypeScript.
- Data & auth: Neon Auth + Neon Postgres; Drizzle ORM with forward-only migrations and idempotent import of existing browser-only records.
- Coach: Optional Groq provider with timeout and schema-validated mock fallback.
- Deploy: Vercel with Neon marketplace integration; camera requires HTTPS or localhost.
Pipeline (simplified): Guide-aligned capture → quality checks → browser YOLO inference → validated detections → deterministic 0–100 score → fixed safety review → coach wording → save to Neon
Challenges we ran into
- Keeping the product useful while remaining explicitly non-diagnostic.
- Making browser-based inference feel fast and reliable across camera/lighting variability.
- Ensuring the LLM coach cannot overwrite or reinterpret detections, scores, or safety rules.
- Designing honest, polished empty-first-run states (no fake seed data).
- Dataset limitations: small, imbalanced training exports and limited demographic/generalization coverage.
Accomplishments that we're proud of
- End-to-end authenticated app with on-device vision — no cloud vision API required.
- Safety firewall: coach receives only fixed score/detections/safety output and cannot change them.
- Polished, brand-forward UI (ScoreOrb, guided capture, mobile bottom nav) and a clear demo path.
- Honest data UX: import is idempotent and no fake history is presented as real user data.
- Reusable architecture: deterministic scoring, Zod schemas, and a clear separation between detection, scoring, safety, and coach layers.
What we learned
- Technical depth is more credible when the UI names the systems (on-device YOLO, deterministic scoring, safety-first).
- Medical-adjacent tooling builds trust by clearly stating limitations and what it will not do.
- A carefully designed empty state is a product feature, not a placeholder.
- Browser inference improves privacy but needs extra engineering to be trustworthy in production (pose/quality checks, server-side validation).
What's next for CrownScore
- Stronger image-quality and head-pose checks to reduce false subjects and improve repeatability.
- Optional trusted server-side re-inference for higher assurance and better auditability.
- Improve dataset balance, evaluation, and expanded test coverage (see KNOWN_LIMITATIONS).
- Offline write queue and richer account export / portability controls.
- Upgrade and harden Neon Auth SDK dependency once compatible releases are available.
Built With
- drizzleorm
- groq
- neon
- next.js
- onnx
- react
- tailwindcss
- typescript
- vercel
- yolov8
- zod
Log in or sign up for Devpost to join the conversation.