The science
Every claim here
is a number you
can check.
Accuracy across skin tones is not a matter of good intentions. It is a consequence of which colour space you measure in, which pixels you sample, and what you refuse to average.
This page sets out the whole method — the sampling geometry, the conversion, the classification, and the exact thresholds a match has to clear. Nothing below is proprietary. It is published colour science, applied properly.
01 · Colour space
RGB is a storage
format, not a
measurement.
Most shade finders compare colours as RGB triplets, treating red, green and blue as equally important. Human vision does not. Your eye takes roughly 71% of its brightness signal from green and only 7% from blue.
So two shades can sit exactly the same distance apart in RGB and look nothing alike — which is how a tool can be confident it found your match and still be visibly wrong.
Same RGB distance, different to the eye
#a0785f
Reference tone
#a0965f
Green +30
#a0787d
Blue +30
- RGB distance
- 30 · 30
- Identical, by construction
- Perceptual ΔE
- 21.4 · 17.0
- 26% apart, and you can see it
CIELAB fixes this by construction. It is built so that equal numeric distance means equal visible difference, anywhere in the space — light or deep, warm or cool. Measure there, and one tolerance can hold honestly for every complexion.
02 · Sampling
The Triangle
of Truth.
MediaPipe places 478 landmarks on the face, on your device, in under half a second. Only nine of them matter for colour. They define three patches of skin least disturbed by shadow, expression and cosmetics — both cheeks and the centre forehead.
40%
Left cheek
#50 · #116 · #123
40%
Right cheek
#280 · #345 · #352
20%
Forehead
#10 · #151 · #337
Cheeks carry the weight because they are the largest continuous area of undisturbed skin. The forehead corroborates them at a lower weight, since it catches more overhead light.
Shadow discarded
Luminance variance > 30A shaded jaw reads as a deeper skin tone. Rejecting high-variance pixels stops ambient light from being mistaken for pigment.
Specular highlight discarded
L* > 90 in CIELABReflected light is the colour of the light source, not of skin. Highlights are removed before averaging.
Outliers discarded
Median, not meanA blemish, a stray hair or a hard edge would drag an average. The median of each cluster ignores them.
03 · Conversion
Four transforms, no guesswork.
Per ISO 11664-4:2008, run client-side in under 50 milliseconds. The output is precise to two decimal places, and the same input always yields the same reading.
- sRGB
C / 255Channels normalised to a 0–1 range.
- Linear RGB
C ≤ 0.04045 ? C / 12.92 : ((C + 0.055) / 1.055)^2.4Gamma removed. Screen values are stored non-linearly; light does not behave that way.
- XYZ
M(D65) · [R G B]Projected into a device-independent space under the D65 standard daylight illuminant.
- CIELAB
L* = 116·f(Y/Yn) − 16 a* = 500·(f(X/Xn) − f(Y/Yn)) b* = 200·(f(Y/Yn) − f(Z/Zn))Lightness, then the green–red and blue–yellow axes. This is where the eye and the arithmetic finally agree.
Validated against known references: pure white returns L* 100, a* 0, b* 0 within ±0.5.
04 · Classification
Two readings,
one answer.
Your CIELAB reading is compared against the reference value for each Monk level, and the nearest wins. A vision model then looks at the image independently and offers its own classification.
Where the two agree within two levels, the model’s reading is used. Where they diverge further, the arithmetic wins — it is the more reliable of the two, and it does not inherit anyone’s training bias. If the model is unreachable, classification proceeds on CIELAB alone at a stated lower confidence.
The rule that matters
When measurement and model disagree, measurement wins. A statistical model trained on the internet has seen far more light faces than deep ones. The arithmetic has not.
| Level | Swatch | L* | a* | b* |
|---|---|---|---|---|
| MST 01 | 94.2 | 1.5 | 5.4 | |
| MST 02 | 92.3 | 2.1 | 7.3 | |
| MST 03 | 93.1 | 0.2 | 14.2 | |
| MST 04 | 87.6 | 0.5 | 17.7 | |
| MST 05 | 77.9 | 3.5 | 23.1 | |
| MST 06 | 55.1 | 7.8 | 26.7 | |
| MST 07 | 42.5 | 12.3 | 20.5 | |
| MST 08 | 30.7 | 11.7 | 13.3 | |
| MST 09 | 21.1 | 2.7 | 6.0 | |
| MST 10 | 14.6 | 1.5 | 3.5 |
Measured from the published Monk scale swatches under the D65 illuminant. Note that lightness is not evenly spaced across the scale — the gap from MST 5 to MST 6 is nearly 23 points of L*, while MST 1 to MST 2 is under 2. Treating the levels as evenly spaced is one of the ways matching goes wrong.
05 · Match quality
What counts as a match.
Each candidate shade is scored by its perceptual distance from your reading, ranked nearest first, with a small bonus where the undertone agrees as well as the tone.
- 0.0 – 1.0PerfectIndistinguishable, even side by side
- 1.0 – 2.0ExcellentBelow the threshold of human perception
- 2.0 – 3.5GoodDetectable only under close comparison
- 3.5 – 5.0FairVisible in daylight
- > 5.0PoorObviously the wrong shade
Distance
ΔE = √( ΔL*² + Δa*² + Δb*² )Score
max(0, 100 − ΔE × 10)Below ΔE 2.0, the difference sits under the threshold of human perception. That is the bar CeySkin aims for — and because it is measured in a perceptually uniform space, it means the same thing at MST 10 as it does at MST 1.
06 · Undertone
Tone tells you
half the story.
Two people can sit at the same Monk level and still need different shades, because lightness says nothing about the colour underneath. That is what the a* and b* axes carry — green to red, and blue to yellow.
- a* > 10, b* > 15
Warm
Golden, peachy and yellow casts. Reads well against gold.
- a* < 5, b* < 10
Cool
Pink, rosy and blue casts. Reads well against silver.
- a* 5–10, b* 10–15
Neutral
A balanced mix, at ease with either metal.
- a* > 5, b* < 0
Olive
A green-earth cast that most matching tools have no category for at all — which is why olive complexions are so often handed the wrong shade.
Chromaticity plane · a* against b*
Dots mark the ten Monk reference tones in the chromaticity plane. Note how they arc — deeper tones carry more red and, past MST 7, progressively less yellow. A model that assumes undertone is constant across the scale will miss this entirely.
07 · Safety
One word,
many names.
Declaring an allergy to fragrance is useless if the filter only looks for the word “fragrance”. Ingredient lists hide the same substance behind regulatory synonyms, trade names and translations.
So each declared allergen expands into its full alias set before anything is compared, matched case-insensitively with whitespace normalised. The check runs in under 10 milliseconds per product.
Zero false negatives
The requirement is 100% recall. A missed allergen is not a ranking error to be tuned away later — it is a reaction. Where recall and precision conflict, recall wins every time.
- FragranceParfumPerfumeAromaEssential oils
- ParabensMethylparabenPropylparabenButylparabenEthylparaben
- SulfatesSLSSLESSodium lauryl sulfateAmmonium lauryl sulfate
A product listing Aqua, Parfum, Glycerin is excluded for anyone who declared fragrance — even though the word never appears.
08 · What we hold ourselves to
Numbers, not adjectives.
Any system can claim to be inclusive. These are the thresholds that decide whether this one ships.
- ≥ 95%
- Classification accuracy
- Held across every Monk level, not averaged across the scale.
- ± 5%
- Maximum parity gap
- The largest permitted spread between MST 1–3 and MST 8–10. This is the number the whole system is judged on.
- < 500ms
- Face mesh on-device
- Measured on a mid-range handset from 2019 or later.
- ≤ 24h
- Photo retention
- Any image sent for cross-checking is deleted automatically by lifecycle policy.
09 · Your photograph
Held briefly,
then gone.
Mapped on your device
Landmark extraction and skin segmentation run entirely on the handset. The map of your face is never uploaded.
Deleted within 24 hours
Where a photo is sent for AI cross-checking, it is held under a lifecycle policy that deletes it automatically inside a day. It is never used for training.
No identity, no account
No facial recognition, no biometric template, no name required. The app is keyed to a device identifier, not to you.
Encrypted throughout
TLS 1.3 in transit, AES-256 at rest. Deletion and export on request, under GDPR and CCPA.
Now see it applied.
The method above is only worth something if it reaches your hands. Join the waitlist and we will write when early access opens for your level.