Predictive clinical skin intelligence from a single selfie. Built for the YouCam Skin AI Hackathon — Skin AI track.
See ROADMAP.md for milestones and submission status.
- Capture / Upload — take a photo with the built-in camera or upload an existing image.
- Face validation — checks resolution, aspect ratio, brightness, and glasses before analysis.
- AI skin diagnosis — sends the image to the YouCam Skin Analysis API and returns multi-concern severity scores, per-zone scores, and segmentation masks.
- Results & coaching — shows overall score, skin age, skin types, concern breakdowns, zone map, ingredient targeting, treatment simulation, and a personalized care plan generated by Qwen via DashScope.
- Downloadable report — export a branded HTML report with all scan data, or print/save as PDF directly from the browser.
- History & progress — signed-in users can browse past scans on the main dashboard, compare week-over-week concern changes, and request a progress review.
| Feature | Status |
|---|---|
| Camera capture, upload, full UI flow | Real, fully working |
| Face validation & glasses detection | Real — YouCam face-attribute API + local heuristic fallback |
Skin condition analysis (/api/analyze) |
Live by default when YOUCAM_API_KEY is set; falls back to a local mock if missing |
Treatment simulation (/api/simulate) |
Live by default when YOUCAM_API_KEY is set; falls back to a local curve mock if missing |
Downloadable report (/api/download-report) |
Real — generates a styled HTML report on the server for download/print |
Personal recommendation narrative (/api/recommend) |
Real — live call to Qwen via DashScope (DASHSCOPE_API_KEY) |
Personalized plan (/api/qwen) |
Real — live call to Qwen via DashScope (DASHSCOPE_API_KEY) |
Progress review (/api/progress-review) |
Real — generated from the user's saved scan history |
| Supabase auth (sign up / sign in) | Real, fully working |
| Scan history + week-over-week comparison | Real, fully working |
Product recommendations (/api/products/recommend) |
Real — reads from Supabase products / product_availability tables |
The mock fallbacks are isolated in lib/skinAnalysis.js and clearly commented. The app is structured so swapping from mock to live is a configuration change, not a rewrite.
- Uploaded photos are stored as data URLs in memory during the session.
- When saving a scan, the image is uploaded to Supabase Storage (
scan-photosbucket) and a public URL is saved to thescanstable. - Past scan images are loaded from Supabase Storage when viewing history.
- YouCam API responses are parsed into a normalized shape:
concerns: object mapping concern slugs to severity scores (0–100)zones: object mapping face zones to aggregate scoresmasks: object mapping concern keys to segmentation mask image URLsoverall: single aggregate scoreskinAge: estimated skin ageskinTypes: array of skin type descriptors by region
- This data is persisted to the
scanstable in Supabase alongside the image URL, routine text, lifestyle preferences, and Qwen plan.
- Qwen / DashScope is used for:
- Personal recommendation narrative (
/api/recommend) - Personalized care plan (
/api/qwen) - Progress review (
/api/progress-review)
- Personal recommendation narrative (
- These calls are made server-side only. API keys are never exposed to the browser.
- Supabase Auth handles sign-up / sign-in.
- Auth state is refreshed via Next.js middleware.
- Row Level Security (RLS) ensures users can only access their own scans.
app/
page.js → landing page (OurSkinOurFuture branding)
layout.js → global layout + metadata
auth/ → Supabase sign in / sign up
dashboard/ → main app: capture, validate, analyze, results, save, compare
api/
analyze/ → YouCam skin-analysis proxy + GET color-tone/face-attribute
simulate/ → YouCam skin-simulation proxy
recommend/ → Qwen personal narrative
qwen/ → Qwen care plan generator
progress-review/ → Qwen progress review from scan history
glasses-detect/ → Heuristic glasses detection + Teachable Machine fallback
products/recommend/ → Product lookup from Supabase
download-report/ → Server-side HTML report generator
components/ → shared UI: FaceGuide, CameraCapture, FaceZoneMap, TimelineSlider, ScanComparison, IngredientCard
lib/
skinAnalysis.js → domain logic: labels, ingredients, mocks, comparison math, routine builder
youcam.js → YouCam Skin AI + Simulation client
supabase/ → browser + server Supabase client setup
supabase/
schema.sql → full database schema (scans, storage, RLS)
01_schema.sql → full schema with products, ingredients, brands tables
02_seed_data.sql → seed data for products and ingredients
migration_*.sql → additive migrations for new columns
middleware.js → keeps Supabase auth session refreshed
ROADMAP.md → full project plan, milestones, and what's left
Prerequisites: Node.js 18+, Bun, a Supabase project, and (optionally) DashScope + YouCam API keys.
bun installCopy .env.example to .env.local and fill in:
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_SUPABASE_URL |
Supabase project URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Supabase anon/public key |
SUPABASE_SERVICE_ROLE_KEY |
Supabase service role key (server-only) |
DASHSCOPE_API_KEY |
Qwen / DashScope API key for recommendations, plans, progress reviews |
YOUCAM_API_KEY |
YouCam Skin AI API key for live analysis and simulation |
- Create a project at supabase.com.
- In SQL Editor → New Query, paste the contents of
supabase/schema.sqland run it. - If the table already existed before recent migrations, run the SQL files in
supabase/migration_*.sqlin order. - Go to Settings → API and copy the Project URL + anon key into
.env.local. - In Authentication → URL Configuration, set your Site URL and Redirect URLs for production.
bun devVisit http://localhost:3000.
bun lint
bun typecheck
bun build- Push the repo to GitHub.
- Import into Vercel.
- Add the environment variables from step 2 above.
- Deploy.
Every push to main triggers an automatic redeploy.
Update Supabase Authentication → URL Configuration with your production Vercel URL so email confirmation links redirect correctly.
- Client sends a
data:image URL to/api/analyze. - Server uploads the image to Supabase Storage to obtain a publicly accessible URL.
- Server calls YouCam
POST /s2s/v2.0/task/skin-analysiswith the public URL. - YouCam processes the image and returns a
task_id. - Server polls
GET /s2s/v2.0/task/skin-analysis/{task_id}until completion. - Results are normalized into
concerns,zones,masks,overall,skinAge, andskinTypes.
If YOUCAM_API_KEY is missing, the route falls back to lib/skinAnalysis.js:mockSkinAnalysis() with a deterministic seed so scores are stable per image.
- Primary path: YouCam
face-attributetask (wearing_glasses,glasses,has_glassesfields). - Fallback: client-side heuristic sampling the eye-region band for dark-pixel ratio, symmetry, and nose-bridge lift.
- Calls YouCam
skin-simulationtask. - If unavailable or returning errors, falls back to a local curve generator that interpolates concern scores from week 0 to week 12.
- Persistence: The first time a simulation runs, its projected scores and image URL are saved to the
scans.simulationJSONB column. When viewing a past scan,TimelineSliderreuses the saved simulation instead of calling the API again.
- Server-side calls to DashScope's Qwen model.
- Prompts include the user's exact concern scores, routine, and lifestyle preferences.
- Responses are streamed back as plain text and rendered in the dashboard.
- Client sends the current analysis state to the server.
- Server generates a styled HTML report with concern severity tables, zone scores, simulation results, recommendations, and care plan.
- Returns the file with a
Content-Disposition: attachmentheader so the browser downloads it reliably. - The downloaded HTML includes a Print / Save as PDF button for PDF export via the browser print dialog.
- Server-side API keys:
YOUCAM_API_KEY,DASHSCOPE_API_KEY, andSUPABASE_SERVICE_ROLE_KEYare only ever read on the server. The browser never sees them. - Supabase Storage for images: Uploading to Supabase instead of passing pre-signed S3 URLs avoids download-failure edge cases with the YouCam API.
- Deterministic mock seed: When YouCam is offline,
mockSkinAnalysis(seed)uses the image payload size + name length as a seed so the same photo always returns the same scores. - Single normalized schema:
concernsandzonesuse the same shape in the database and UI, so there's no transformation layer between storage and rendering.
See ROADMAP.md for detailed milestones, submission checklist, and known limitations.