Inspiration
Skin AI has a skin-tone problem. When researchers tested three state-of-the-art dermatology AI models on a curated image set with diverse skin tones, they found "substantial limitations", "particularly on dark skin tones" (Daneshjou et al., Science Advances, 2022). Skin-lightening products are a cross-border problem too. The WHO says mercury-containing skin-lightening products "have been banned in many countries", yet "even in countries where tight controls exist, such products may be found being advertised and available to consumers via the Internet" (WHO, Mercury and health). The US FDA warns that mercury skin creams "usually are manufactured abroad and sold illegally in the United States" (FDA). The Minamata Convention on Mercury listed cosmetics with more than 1 ppm of mercury for phase-out, and its parties have since agreed to remove even that limit (Minamata Convention).
People don't wonder about their skin in the abstract. They wonder in front of a shelf, holding a jar whose label they can't read. They wonder three weeks into a serum, in front of the mirror, unsure whether anything has changed. In Kenya, our concrete example, that moment has a sharper edge: in November 2022 the Kenya Bureau of Standards (KEBS) reminded the public that 131 skin-lightening products (creams, lotions, gels and soaps containing hydroquinone, steroids or mercury) remain banned under gazette notices of 1998 and 2000 (Kenyans.co.ke, 1 Nov 2022).
Most skin apps answer "what does my skin score today?". That is the wrong question. A single score swings with the light, the camera and the angle, and on deeper skin some features (redness) can't be read from an ordinary photo at all. The honest question is: did the look of my skin change by more than a photo can wobble, while I was using this product?
Who it's for
People with melanin-rich skin, wherever they live, who buy over-the-counter skincare from pharmacies and markets, want to know whether a product is safe and whether it is doing anything, and would rather not be sold to.
Consumer and retail value
- For shoppers: before paying, one check says whether the product contains an ingredient that is banned in cosmetics or belongs in medicines, and which evidence-backed actives it really has. After buying, a product test shows in photos whether the look changed by more than a photo can wobble, so the next purchase is based on what happened rather than on hope. MediQuest Skin sells nothing and ranks no brands, so its answers are not advertising.
- For pharmacies and retailers: the same flow fits a shop. The label check works at the shelf with nothing uploaded, and a product test is an honest after-purchase follow-up, measured the same way for every customer and every skin tone, that a shop could offer instead of a flattering score.
What it does — three moments
1. Before you buy. Point the camera at the label. The text is read on the phone, you check the list it read against the label, and the photo is never uploaded or stored; pasting the list still works. On the phone, with nothing sent anywhere, MediQuest Skin:
- flags ingredients banned in cosmetics in Kenya or that belong only in medicines (mercury compounds, hydroquinone, corticosteroids, medicine-strength retinoids) — and then offers nothing else for that product;
- lists which evidence-backed over-the-counter actives it contains (niacinamide, vitamin C and its derivatives, azelaic acid, salicylic acid, lactic acid, ceramides, glycerin/hyaluronic acid, UV filters), using exact INCI synonyms only, and notes when an active sits so late in the list that it may be a small amount, or when UV filters are present but the label still has to say broad spectrum and SPF 30+;
- says how those actives fit your two lowest-scoring features from your last check-in, and warns if the product overlaps something you're already testing ("starting this now will blur both results");
- one tap pre-fills a product test for it.
2. While you use it. A product test is a small experiment: a baseline check-in in even front light, a planned 4, 6 or 8 weeks (most routines need several weeks before the look changes), weekly selfies, and a closing check-in in the last week. A Next step card at the top always says the one thing to do now: take your baseline, your next check-in is on 6 Oct — use even front light, like your baseline, your last photo was too dim so retake it, or your test is ready to read. Running two tests at once is allowed but called out on every readout, because then nobody can tell which product did what.
3. Did it work? The readout compares the closing check-in with the baseline, feature by feature, against a noise band: "Visible pores: index up +8.7 — beyond normal photo variation"; "Shine: within normal photo variation". It refuses to compare photos taken in different light or too close together, and says why. It hides features photos can't measure for your skin tone. It never says a product "works" or that you should keep or stop it: "This shows how the look changed in photos, not why. Whether to keep using it is your call."
A clinician option is always on screen and is never triggered by a score.
How we use YouCam
- AI Skin Analysis v2.1 (SD) — every check-in submits one photo for 8 concerns (dark spots/uneven tone, texture, shine, pores, blemishes, redness, hydration, radiance). We use the engine's unadjusted
raw_scoreonly and drop the display score the vendor documents as flattering, andskin_age. Every vendor label is relabelled to cosmetic "look of" wording. - AI Fitzpatrick Skin Type — a one-off sun-sensitivity estimate the user confirms or corrects, used only for sun-care copy. It never decides which scores are shown: that is the user's self-reported Monk Skin Tone band.
- Task delete — called after every check-in, including failed and timed-out ones, with the result shown in the history. Every task also goes into a server-only queue (just the vendor's task id: no scores, no user id) in the same write that records it (only a server crash between the vendor's reply and that write, a database failure at that write when the immediate delete fails too, or a reply that never arrives, could leave one untracked; the day's slot then stays spent, and the vendor removes the uploaded photo under its own retention: its documentation says 24 hours in one place and up to 30 days in another), so a daily job deletes it even if the tab was closed mid-analysis and nobody opens the journal again (never within the first hour, so a result read late from a tab left in the background is still kept); it leaves the queue only when the vendor confirms the deletion, and that confirmation is kept as proof. Each time the journal loads, it also retries its unconfirmed deletions, oldest first, three at a time (the server asks the vendor about each at most once every 10 minutes, and these extra tries never use up the daily job's). Deleting a check-in or all skin data queues its task the same way. A task the vendor still hasn't confirmed after 60 tries (one a day from the daily job, plus at most one more a day when its check-in, the journal or the account is deleted, or a sun-sensitivity estimate ends; the retries made when the journal is opened don't count) is moved by the daily job to a server-only list for follow-up by hand, and that job's health check flags it.
- Credit balance — before every live call the server reads the unit balance (
GET /s2s/v1.0/client/credit). If the call's cost (12 units for a skin analysis, the vendor's documented price for 5–8 concerns, and 10 for a Fitzpatrick estimate) would take the balance below the reserve floor (SKIN_UNITS_RESERVE_FLOOR, 400 in production during judging), live analysis pauses for public accounts and says so; it never makes up a result, and the public sample keeps working. The judges' demo accounts may spend that reserve, so sign-ups cannot use up the judging window. Each request checks the balance on its own, so calls already in flight can each take one call's cost below the floor; size the floor to cover that. If the balance can't be read, live analysis stops rather than guess. A per-user daily cap (SKIN_UNITS_DAILY_CAP, 3 check-ins) applies as well. The cost is read live fromGET /s2s/v2.0/credit/feature-costand matched to the configured concern set (the live list's "AI Skin Analysis V2.1 SD (5-8 concerns)": 12 units for our 8 concerns; Fitzpatrick V1.0: 10), cached for 30 minutes, falling back to those fixed costs only if the lookup fails or is slow.
The creative part is not the call; it's what we refuse to do with the number. The engine's score becomes a measurement with an error bar (a provisional noise band, one width for every tone until per-tone accuracy is published, with redness hidden on Monk 8–10), comparisons only happen between check-ins taken in matching light and far enough apart, and the unit of meaning is a product experiment rather than a daily score.
Privacy by design
- The photo is re-encoded in the browser and stripped of metadata (EXIF, GPS, comments) again on the server, sent to YouCam for analysis, deleted there afterwards, and never stored by us. If the upload succeeds but the analysis request then fails, there is no task to delete (task delete is the API's only delete), and Perfect Corp's own retention applies. Its documentation disagrees with itself: one page says "Any files you upload are stored on our server for 24 hours, and then they're automatically deleted" (YouCam API reference); the current file-retention page gives 30 days (File retention period). We say so in the consent text rather than promise the shorter figure.
- Scores, product names, ingredient lists and notes are encrypted with a per-user key. The self-chosen tone band, the sun-sensitivity type, the photo’s brightness and size and test dates and targets are stored unencrypted so the server can compare check-ins; Firestore rules deny all client access to the journal.
- A consent sheet names Perfect Corp, says processing happens outside Kenya and mentions facial geometry before any selfie is taken.
- The product check runs entirely on the device. One button (with a confirmation) deletes everything (check-ins, tests, consent, vendor copies); only that day's usage counters (check-ins and sun-sensitivity estimates started) stay, so deleting cannot reset the daily caps, and, if the one billed sun-sensitivity estimate was used, a flag saying so, so a second one is never offered; and the ids of any photo task Perfect Corp had not confirmed deleting at the time (server-only, so the daily job and a later deletion keep asking; they go when a later deletion finds them confirmed, or with the account): a small record with no consent and no skin data. The counters count nothing after that day and go with the next delete; the estimate flag stays until the account is deleted. The audit log is kept too, even after the account is deleted, as a tamper-evident security record for the whole platform: when check-ins and estimates were taken (with the date, IP address and browser) and when consent was given or withdrawn; it holds no photo, score or note. The account export includes the skin journal, decrypted: the journal record (consent and settings), each check-in as the journal shows it (scores, note, the photo's light and size, deletion status) and the tests; each check-in's internal bookkeeping (the engine's raw error code, the consent version and provenance tags it was recorded under, the deletion-queue flag) is left out.
Honesty by design
- Noise-aware verdicts only: a change smaller than ordinary photo variation is reported as exactly that, never as progress or a setback.
- Redness is hidden for Monk 8–10 (melanin masks it in ordinary photos). The reliability table is labelled provisional until our own skin-tone consistency pilot runs.
- A vocabulary test fails the test suite if any journal string in any of 7 languages contains an English stem from our banned list (the translations were also reviewed by hand) (
diagnose,detect,acne,treat,cure,severity,risk,healthy…), and a check that blocks the build applies the same rule to this submission text and the video script. Product-test copy describes how the look changed, never that a product works. - No score can trigger a referral; the clinician card is static.
How we built it
Next.js and TypeScript on the existing MediQuestAI platform; Firebase (Firestore via the Admin SDK only); the YouCam REST APIs from a server-only adapter; all journal rules (comparability, noise bands, test state machine, next-step planner, INCI matcher) as pure shared functions with Jest tests, so the signed-in journal, the public sample and the tests can never disagree. Translated into English, Kiswahili, French, Arabic, Spanish, German and Italian.
Challenges
- Making "did it change?" honest on deeper skin when per-tone accuracy for this engine version is unpublished: we chose a conservative provisional noise band and suppression, and planned a pilot to replace it.
- Not letting a week-one photo be read as the result, and not letting a dim photo turn a comparison into a lighting comparison.
- INCI lists are messy; the matcher only accepts true synonyms (betaine salicylate is not salicylic acid; ethylhexyl salicylate is a UV filter).
What's next
- Run the 9-volunteer Monk-band consistency pilot and replace the provisional noise bands with measured ones.
How it was significantly updated during the submission period
MediQuestAI existed before the hackathon as a telehealth platform. The baseline tag youcam-baseline marks commit 7f389d9, the last commit before the submission period began; everything in youcam-baseline...youcam-submission was built during it. That includes the whole of MediQuest Skin: the consent sheet, the YouCam Skin Analysis v2.1 and Fitzpatrick integrations, the noise-aware journal, the per-tone reliability table, the public sample journal, and — in the final stretch — the product-test experiment ("Is it working?"), the next-step planner and the before-you-buy product check that turns the on-device ingredient safety check into a product check with fit and conflict warnings. Reviewers can see the exact diff with git diff youcam-baseline...youcam-submission; its MediQuest Skin part is git diff youcam-baseline...youcam-submission -- shared/skin web/lib/skin web/components/skin-journal "web/app/[locale]/dashboard/skin-journal" "web/app/[locale]/skin-journal" web/app/api/skin-journal web/app/api/ai/skin-analysis web/app/api/ai/skin-type firestore.rules. On GitHub: https://github.com/mlsniperpro/mediquestai/compare/youcam-baseline...youcam-submission
The repository is private because it holds the whole telehealth platform. Judges get access through the private repository invite sent to Perfect Corp's judging address; the compare link works once that invite is accepted.
Built With
- firebase
- jest
- next.js
- react
- tailwindcss
- typescript
- youcam-ai-fitzpatrick-skin-type
- youcam-ai-skin-analysis
- zod
Log in or sign up for Devpost to join the conversation.