-
-
Same box, same hair. Left is what the box promises. Right is what it will probably do to this hair.
-
Of 202 real shades, 178 are lighter than naturally dark hair. By the brands' own lift limits, only 48 should reach the box colour.
-
It starts with one selfie while you hold any white paper. The paper lets Bense correct for the room light.
-
Box dye lifts natural hair only 2 to 3 levels. That number comes from the brands' own guides, not from us.
-
YouCam's Hair Color API paints only the hair green. Bense measures how light those pixels are. That gives your real level.
-
Then the question no box asks: has your hair been dyed before? Then pick the box. These are example shades, not real products.
-
Left: YouCam's render. Right: Bense's finish, which keeps each strand's own light and shade. It runs on the phone.
-
Two Hair Color renders, side by side on your own photo: what the box promises, and what you will probably get.
-
The honest answer: this box falls 5 levels short and will turn warm. Every rule links to its source, with a closer box to buy.
-
Safety questions come first. A past reaction, black henna, or use on brows or lashes stops the recommendation.
-
Six YouCam APIs: Hair Color, Facial Color Tones, and Hair Type, Frizziness, Length and Density. The Bense server calls them all.
-
For the side photos, the phone tracks your head angle, tells you where to turn, and takes the photo when it's right.
-
Bense (本色, "your own true colour"): see what a box dye will really do to your hair before you buy it.
Inspiration
My girlfriend dyed her hair at home. She didn't like how it came out, and she was upset for days. I wanted to know if that was bad luck. It wasn't. The box did exactly what box dye does. The picture on the front just doesn't tell you.
Box dye isn't paint. It's see-through, so what you get depends on the hair you already have. The brands say so themselves, in the small print:
- A box can lighten natural hair only about 2 to 3 levels. Black hair will not become blonde.
- Dye can't lift dye. If your hair is already coloured, another box won't make it lighter.
- When dark hair lightens, warm red and orange show through. That's why "ash blonde" often comes out brassy.
- Ash over yellow or lightened hair can turn green.
People know they're guessing. A major hair-colour brand's own head of at-home colour said up to 70% of at-home colour users are unsure which shade suits them (The Drum, 2019). In a survey of 2,000 US women dyeing at home, 47.8% were worried the result wouldn't be what they wanted (Premium Beauty News, 2021). And fixing it is expensive: a salon colour correction runs $200 to $800 and can take several visits.
Every online try-on I tried paints the box colour straight onto your photo. None of them asked what colour my hair is now, or whether it had been dyed before.
So I counted. I loaded 202 real shades from two of the biggest box-dye brands, as sold in India and the USA. From naturally dark hair (level 2), 178 of those boxes are lighter than the hair. Using the brands' own lift limit, only 48 of the 178 can actually reach the colour on the box. The other 130 fall short, and the box picture won't tell you.
What it does
Bense (本色, "your own true colour") shows what a box dye will really do to your hair, before you buy it.
- One selfie, holding any plain white paper.
- It measures your real hair level. YouCam's Hair Color API paints only the hair green. The phone then measures how light exactly those pixels are in the original photo and turns that into a level from 1 (black) to 10 (lightest blonde). The white paper corrects for the room light. You always confirm the level.
- The question no box asks: has your hair been dyed before? Then you pick the box you were going to buy, from example shades or 202 real shelf shades (India and USA).
- Promise vs probable. Two Hair Color renders run in parallel on your own photo: what the box promises, and what your hair will probably do. A slider compares them, and a chart shows your level now, the likely result and the box promise, with the gap marked.
- The honest answer. On my own selfie: level 3 hair and a 9.1 very light ash blonde box. Bense predicts level 4: five levels short, and it will go warm. Every rule links to its source, and Bense shows boxes that the same rules expect to actually work.
- Safety comes first. Three questions from the dye label (a past reaction, a "black henna" tattoo, use on brows or lashes) stop the at-home suggestion, whatever the colour maths says. Every result reminds you to do the skin test in the box.
Optional hair profile. YouCam measures hair type, frizz, length and density from four photos. These APIs need exact head angles, so Bense watches your head on the phone itself and talks you into the pose ("Turn more to your right"), vibrates, and takes the photo when you're there.
Demo mode (/demo) shows the three classic box-dye surprises (the brass surprise, dye can't lift dye, the green surprise) with no camera and no API key. Every result in the app is labelled Live YouCam, Calculated by rules or Demo example, so nobody mistakes an example for their own result.
How I built it
Bense is a phone web app (PWA: React 19, TypeScript, Vite) with a small Fastify server. The YouCam API key lives only on the server; the phone never sees it. Photos are never stored or logged.
Six YouCam APIs, about 30 units for a full session:
| API | What it does in Bense | Units |
|---|---|---|
| Hair Color (green mask) | Marks exactly which pixels are hair, for the level measurement | 1 |
| Hair Color (box promise) | The box shade on your own hair | 1 |
| Hair Color (likely result) | The predicted colour on your own hair | 1 |
| Facial Color Tones | Colour family as a cross-check, plus face and lighting quality | 20 |
| Hair Type + Hair Frizziness | Curl pattern and frizz, from front and both 45° sides | 2 + 2 |
| Hair Length + Hair Density | Length band from the front; density from a head-down photo | 2 + 1 |
Every photo goes up through the File API, and every task is polled to completion.
- Hair Color as a measuring tool. YouCam has no "hair level" API, but Hair Color finds hair very precisely. I ask it to paint the hair pure green (
#00FF00), use that render as a mask, and take the median lightness (CIE L*) of the same pixels in the original photo. One level is about 7 L*. It costs 1 unit. - The prediction is plain rules, not an AI guess: the lift limit, dye can't lift dye, the order in which warm pigment appears, and how far ash can cancel it. Semi- and demi-permanent boxes are predicted not to lighten at all. Each rule shows its source in the app.
- Natural finishing. Raw renders looked painted on. Bense keeps YouCam's colour but rebuilds every hair pixel from the photo's own light and shade, strand by strand, in a Web Worker on the phone. It takes about 1 to 2.5 seconds and costs zero extra units. It also never shows more lightening than the prediction allows.
- Live pose guide. MediaPipe Face Landmarker runs on the phone, and no camera frames leave it. It speaks the instruction, vibrates, and auto-captures after a 1.5 s hold.
- Built for real use: calls run in parallel, a failed task is retried once, a repeated request joins the one already running so you never pay twice, and the hosted version checks the unit balance before a check starts, so a check never stops halfway.
- Measured in the browser: colour reading 12.3 s, two renders 12.1 s, the whole flow on a phone 27.9 s.
- Tests: 211 unit and server tests and 34 browser tests at phone width, with YouCam mocked, plus live smoke scripts.
Challenges I ran into
- Facial Color Tones returns a colour name, not a measurement. It picks one of seven swatches. Black hair came back "Auburn", and so did a clearly blonde sample. It was usable on only 4 of 7 test portraits. So I built my own level measurement from the hair pixels, and the colour name became a cross-check.
- Hair Color can't fully lighten dark hair in a picture. Brown hair at L* 21 reached only 43 with a level-9 target. So the numbers carry the size of the gap, and the pictures carry the tone.
- Renders looked flat and painted.
shine_intensityat 0, 50 and 80 made no visible difference, which is why I wrote the strand-by-strand finishing. - The raw render lightened box-dyed black hair to brown, even when the rules say almost nothing will change. The finishing now caps the picture at the predicted lift.
- Hair Length rejected 3 of 3 slightly turned faces (
error_face_angle_invalid). That's why the live pose guide exists. - Occasional
unknown_internal_error: one automatic retry, and a separate upload for each task group. - Box codes are rarely explained on the brands' sites. I use the usual convention (the first number is the level), say so on every shade, and left out 96 products with no whole level, and all bleach kits.
Accomplishments that I'm proud of
- Using Hair Color as a measuring instrument. The same photo measured L* 58.5 and 58.58 in two renders hours apart: level 8 both times, for 1 unit each.
- "48 of 178": a number no brand publishes, worked out from the brands' own rules and real shelf shades.
- Renders that look like hair, not paint, for zero extra units.
- Honesty built into the screens: every result says whether it is live, calculated or a demo, and every rule links to its source.
What I learned
- A try-on that shows you the box answers the wrong question. The useful question is "what will my hair do?"
- Read what an API actually returns. A colour name from seven swatches is not a measurement, and finding that out early changed the whole design.
- Saying what you don't know is part of the product. Bense shows the level as a range you confirm, not a number to trust blindly.
What's next for Bense
- Calibrate the level against 10 to 20 selfies whose levels are graded by hand, and publish the result, good or bad.
- Verify Hair Type and Frizz with real 45° photos.
- A third render: the better box, shown on your own hair.
- A matching brow pencil through YouCam Makeup VTO (never brow dye).
- A "Will this box work on me?" button for beauty shops, where the cart swaps to a box that will work, so the shop keeps the sale.
- A
check_box_dyetool for AI shopping assistants, so any assistant can check before it recommends a dye.
Built With
- docker
- fastify
- mediapipe
- node.js
- perfect-corp
- playwright
- pwa
- react
- typescript
- vite
- vitest
- web-workers
- youcam-api
- youcam-facial-color-tones
- youcam-hair-color
- youcam-hair-density-detection
- youcam-hair-frizziness-detection
- youcam-hair-length-detection
- youcam-hair-type-detection
- zod
Log in or sign up for Devpost to join the conversation.