Inspiration
Skincare labels are confusing. Most people don't know that some ingredients, like retinol and glycolic acid are excellent individually but can irritate or damage the skin barrier when combined. The information exists, but it's buried in dermatology articles and ingredient databases. We wanted to make it instant: paste two products, get a clear answer. SkinWise was built to solve that specific problem.
What it does
SkinWise checks if two skincare products are safe to use together. You paste the ingredients of two products or pick from presets like The Ordinary Retinol and Paula's Choice BHA and the app returns one of four verdicts: 🔴 Conflict — do not combine 🟡 Caution — can use together with care 🟢 Safe — safe to layer ⚪ Unknown — not enough information to evaluate For each verdict, it shows the specific ingredients that conflict, a plain-English explanation of why, and a practical schedule (layer together, AM/PM split, or alternate nights). It also shows a safety disclaimer that this is an educational tool, not medical advice.
How we built it
The system has two parts.
The rule engine (Next.js + TypeScript). We defined 13 ingredient categories — retinoids, direct acids, vitamin C, peptides, antioxidants, barrier support, humectants, and so on — and about 20 interaction rules between them. When two products are submitted, the engine detects which categories their ingredients belong to and looks up the rules. The final verdict is the worst interaction found.
The ML classifier (Python + FastAPI + SetFit). A few-shot text classifier trained on ~250 labeled skincare ingredients. It identifies the category of an unfamiliar ingredient. Runs as a separate service and is called by the Next.js backend.
The fallback layer. If the ML service is unavailable, a regex matcher built into the backend handles common ingredients. The app never breaks — it just runs "differently."
We also built a guardrail: if the ML predicts a category for an ingredient we already know is inactive (like citric acid in a cleanser), the deterministic rules override it. This prevents the ML from producing false positives.
Challenges we ran into
Training with a small dataset. With only ~250 labeled ingredients, the model initially misclassified common ingredients. For example, it labeled Citric Acid as an exfoliant when in a cleanser it's almost always just a pH adjuster. We fixed this by balancing class weights, adding synonym variations, and building the override layer so known ingredients always take priority.
Deploying PyTorch to free hosting. This was the hardest part. Render's free tier has 512 MB of RAM, and PyTorch alone uses about 600 MB — the service crashed with out-of-memory errors before it could even start. Hugging Face Spaces also had startup issues because ZeroGPU requires a specific decorator to initialize. We solved this by using a lazy-loading FastAPI service that only loads the model on the first request, which reduces memory pressure during startup.
Reliability on free tiers. Because free hosting is not reliable, the deployed version uses the regex fallback by default. The full ML-powered version runs locally for the demo. This is a real constraint of building on free infrastructure — and it's why we built the fallback in the first place.
Accomplishments that we're proud of
Building a hybrid system that works. The rule engine handles known interactions deterministically. The ML classifier extends coverage to ingredients we haven't seen. Neither alone would be enough — together they cover both common and edge cases.
Graceful degradation. The app works even when the ML service is down. Users always get a result. This was not an accident — we designed for it from the start.
Honest uncertainty handling. When we can't evaluate a combination, we return "Unknown" instead of defaulting to "Safe." This was a deliberate design choice. It makes the app less impressive on paper but far more trustworthy for a safety-related tool.
A working end-to-end pipeline. From ingredient input to ML classification to rule lookup to rendered verdict, the full flow runs in under a second on local hardware.
What we learned
Deterministic rules beat ML for safety-critical decisions. The ML is helpful for identifying ingredients, but the final verdict must come from rules that can be verified, tested, and audited.
An app should still work when one part fails. If the ML service is unavailable, the app uses regex instead. This isn't a fallback we added later — it's a core design principle.
"Unknown" is better than "Safe." When the system doesn't have enough information, saying so is more responsible than defaulting to a safe verdict. This is especially important when the wrong answer could lead to skin damage.
Deploying ML models is harder than training them. Training took an hour. Deployment took days. The infrastructure for running PyTorch models in production — especially on free tiers — is far less forgiving than the training environment.
What's next for SkinWise
Expand the training data. From ~250 to 1000+ labeled ingredients using sources like INCIDecoder and Open Beauty Facts. More data means fewer misclassifications and broader coverage.
Photo-based ingredient scanning. Let users take a picture of a product label and extract the ingredient list automatically via OCR. This removes the friction of typing or pasting ingredients.
Concentration-aware rules. Currently the app treats ingredients categorically. But 10% niacinamide behaves differently from 2%, and 0.025% tretinoin is very different from 0.1%. Adding concentration parsing would make the verdicts more accurate.
Proper evaluation with held-out data. Test the model on ingredients it has never seen to measure real generalization, not just memorized accuracy. This would give us an honest accuracy number to report instead of a training-set score.
User accounts and routine tracking. Let users save their current routine and check new products against it. This shifts the app from a one-off checker to a daily tool.
Built With
- fastapi
- few-shot-learning
- hugging-face
- machine-learning
- natural-language-processing
- next.js
- python
- pytorch
- scikit-learn
- setfit
- tailwind-css
- typescript
- vercel
- zod
Log in or sign up for Devpost to join the conversation.