Inspiration
Buying skincare and complexion makeup online is guesswork twice over: shoppers don't know which product fits their skin, and they can't see a shade on their own face before paying. Both are classic reasons for abandoned baskets and returns. We wanted one flow that measures first, recommends with visible reasons, lets the shopper see the result on their own photo, and ends in a basket.
What it does
SkinMatch is a web app (built for a desktop browser; the layout also has a narrow-screen breakpoint) that chains two Perfect Corp. YouCam APIs into a guided-selling funnel:
- Selfie — the browser checks and resizes the photo (short side ≥ 480 px, long side ≤ 2048 px) and sends it to our server, which keeps the API key.
- YouCam AI Skin Analysis — 8 concerns (breakouts, pores, texture, hydration, oil, redness, fine lines, dark circles). The report shows YouCam's overall skin score, its estimated skin age, a score per concern, and a detection mask per concern that can be toggled over the photo.
- Routine — a transparent rule engine ranks a fictional demo catalog against the lowest scores and an estimated skin type, and builds a morning and an evening routine. Every product states why it was picked ("for Hydration 76").
- YouCam AI Makeup Virtual Try-On — try a single shade or a whole look (foundation + blush + lip in one task) on the same photo, with a before/after slider.
- Cart → checkout — add one product or the whole routine; a demo checkout summary (no payment is taken).
Consumer value: no more guessing which serum or concealer fits, and a look is checked on the shopper's own face before buying. eCommerce value: a measured, explained recommendation that ends in a routine-sized basket.
How we built it
Plain Node.js (≥ 22.9) server with zero npm dependencies and a vanilla-JS front end (no build step).
YouCam APIs used (server base https://yce-api-01.makeupar.com, Authorization: Bearer <API key>):
| API | Endpoint | Used for |
|---|---|---|
| File API | POST /s2s/v2.0/file → pre-signed PUT |
upload the selfie once; the file_id is reused for later tasks |
| AI Skin Analysis (SD, 8 concerns) | POST /s2s/v2.0/task/skin-analysis, poll GET …/skin-analysis/{task_id} |
concern scores, overall score, skin age, per-concern masks |
| AI Makeup Virtual Try-On | POST /s2s/v2.0/task/makeup-vto, poll GET …/makeup-vto/{task_id} |
shade / full-look try-on |
Engineering around a 1,000-unit budget (the hackathon redeem code): every paid result is cached by sha256(photo) + feature + params; one upload per photo; a whole look is merged into a single Makeup VTO task; in-flight de-duplication so a double click never pays twice; a hard YOUCAM_UNIT_BUDGET guard (cached results keep working at the cap); a unit ledger shown in the header; result images are downloaded immediately because YouCam result links expire after 2 hours. 429 responses are retried with back-off. A "Forget my photo" button deletes the selfie and everything derived from it.
The project was newly created during the Submission Period (it did not exist before; the repository history shows the dates). Tests: a mock end-to-end test (24 checks) and a client contract test against a local fake YouCam server that replays the response shapes we saw from the real API (12 checks).
Challenges we ran into
- The real response differed from our reading of the docs. With
format: "json", Skin Analysis returns the overall score and skin age as extra entries ({type:"all", score},{type:"skin_age", score}) insideresults.output[], plus aresize_imageentry — not as top-level fields. Our first live run parsed them as concerns; we fixed the parser and added the real shapes to the contract test. - PNG behind
.jpg. Mask and try-on files are PNG overlays although the response Content-Type did not say PNG (and try-on result URLs end in.jpg), so the server sniffs file bytes to choose the extension. - Face size rule. The engine rejects a face narrower than 60% of the image width (
error_src_face_too_small). Our first sample portrait was rejected; we re-cropped it. The app shows a friendly message for this error. - Budget. A full skin analysis is 12 units, so repeat clicks, reloads and judge re-runs must not cost anything — hence the cache, de-duplication and budget guard.
Accomplishments that we're proud of
- Two YouCam APIs working end to end in a real run (File API → Skin Analysis → Makeup VTO), including a 3-effect look (foundation, blush, lip gloss) in a single task.
- Recommendations that carry their reason: each product shows which score it answers.
- A complete 4-step flow (selfie → report → routine & try-on → checkout), a mask toggle and a before/after slider.
- The whole demo run costs 13 units by our ledger (12 for the analysis + 1 for the look try-on), and a judge can run everything with no key at all (mock mode).
What we learned
- Identical image bytes gave identical scores in three live runs; re-encoding the same photo (the browser's canvas resize) changed a few scores by a few points. So the app keys its cache on the exact bytes it uploads.
- YouCam's
ui_scoreis a calibrated version ofraw_scoreand the mapping is not the same for every concern, so the app ranks onraw_scoreand showsui_score. - The documentation pages for Makeup VTO show two result shapes (
results.urlandresults[].download_url); the live service returnedresults.url.
What's next
Plans, not built yet: the AI Look VTO and accessory try-on endpoints as further steps; HD skin analysis; in-browser camera capture; replacing the fictional catalog with a retailer's product feed and hand-off to the retailer's checkout; a hosted instance with the key kept server-side.
Limitations (stated up front)
The catalog, brand ("Dewline (demo)") and prices are fictional. The skin-type label is our heuristic from three scores, not a YouCam output. Not medical advice. Checkout is a demo.
Testing instructions (test build — no key, no account, free)
Requires Node.js ≥ 22.9. No npm install (zero dependencies).
git clone https://github.com/kimdooo-a/skinmatch && cd skinmatch
npm start # no .env -> mock mode, http://localhost:5178 (badge: "MOCK · no API calls")
npm test # mock end-to-end test + live-client contract test, both offline
Click: Use sample face → Analyze my skin → See my routine → pick a look → Add whole routine to cart → Checkout.
Mock mode reproduces the documented response shapes and the task lifecycle with locally drawn masks and try-on previews labelled "MOCK". To run against the real YouCam API, put a key in .env (YOUCAM_API_KEY=…, see .env.example) and run npm start again (badge: "LIVE · YouCam API"). The demo video was recorded in live mode.
Generated imagery disclosure
The sample portrait public/samples/sample-face.jpg is an AI-generated image of a fictional person (generated with Google Gemini, then cropped); it is not a real person. The product catalog and brand are fictional.
Links
- Demo video (YouTube, public): https://youtu.be/9tzXaSGC3bE
- Code repository (public, MIT): https://github.com/kimdooo-a/skinmatch
Built With
- claude-code
- edge-tts
- ffmpeg
- javascript
- node.js
- playwright
- youcam-api
Log in or sign up for Devpost to join the conversation.