SkinToneDetector
What skin tone is that person? This tiny node answers in emoji
- image
- Skin Tone
At some point the question in ComfyUI stops being "how do I make this" and becomes "what did I just make." SkinToneDetector is a read node - the rare one that consumes an image and hands back an answer instead of more pixels. Feed it any IMAGE and it tells you which of the five standard emoji skin tones - LIGHT, MEDIUM_LIGHT, MEDIUM, MEDIUM_DARK, DARK - matches the person in frame. That's the entire job, done in a single string.
Honest framing first: this is a small, hobby-grade pack - one node, one meaningful commit, and a README that's barely three paragraphs. Treat its output as a heuristic rather than ground truth. But the niche it fills is real. Batch-annotating a dataset, writing descriptions to disk, or building a workflow that conditionally picks a prompt based on who's in the photo - all of those want a classifier for exactly one property, and this is a zero-training one that runs locally in seconds.
How it actually works
Under the hood it's MediaPipe face detection plus color science. MediaPipe runs with model_selection=1, the "full range" model tuned for faces a few meters from the camera, so it copes with full-body shots rather than demanding a selfie crop. If several faces show up, it scores them - biggest face, weighted by how close it sits to frame center - and analyzes only the winner. The face box gets cropped, converted to HSV, and run through a classic skin mask (hue 0–20, saturation 20+, value 70+) to keep skin pixels and drop hair, eyes, and background. Those pixels move to LAB color space, the darkest 30% by lightness get thrown out as shadows, and the median LAB value is matched to the nearest of five hand-picked reference tones by Euclidean distance.
LAB is the smart part of this. It's roughly perceptually uniform, meaning distance in LAB actually tracks how different two colors look to a human, unlike RGB. Median instead of mean keeps one harsh highlight from dragging the whole result. For a node this small, the mechanism is genuinely more considered than you'd expect.
One input, one output
Inputs: exactly one - image (IMAGE). There are no optional parameters, no thresholds to fiddle with, nothing to misconfigure. Outputs: exactly one - Skin Tone, a STRING that's one of the five buckets above or NOT_DETECTED when no usable face turns up. It's flagged as an output node, so it's designed to sit at the end of a branch and report. The string is the kind of thing you feed into a text-concat or switch node to drive a prompt, or save with a SaveText-style node as metadata. ComfyUI won't let you plug a string into an image socket, so think about the wiring before you expect it to do anything downstream.
Installing it
Easiest route is ComfyUI Manager - search "Skin Tone Detector" and hit install. Otherwise:
cd ComfyUI/custom_nodes
git clone https://github.com/kevinmcmahondev/comfyui-skin-tone-detector
pip install -r comfyui-skin-tone-detector/requirements.txt
Then restart ComfyUI. It needs mediapipe, scikit-image, and opencv-python on top of what ComfyUI already ships, plus numpy/Pillow/torch you almost certainly have. No model downloads - MediaPipe's detection model is bundled with the pip package, which keeps installs light.
Where people get burned
face-recognitionis a trap. It's listed inrequirements.txt, but the node code never imports it - MediaPipe does all the detection.face-recognitiondrags in dlib, which needs a C++ toolchain (CMake + Visual Studio Build Tools on Windows) and fails on a lot of machines for no obvious reason. Ifpip install -rchokes there, install everything except that one package and the node will still work fine.NOT_DETECTEDmeans no face was found, not a bug. Heavily angled profiles, tiny faces in wide shots, or heavy occlusion all confuse the detector.- Adjacent buckets flip under lighting.
MEDIUM_LIGHTvsMEDIUMvsMEDIUM_DARKare fuzzy boundaries in the real world, and a hard light or strong color cast can shift the result one step. The shadow-exclusion and normalization logic helps, but it's a heuristic - if you need pixel-accurate tone classification for training data, sanity-check a sample rather than trusting every output. - Install into ComfyUI's Python, not your system Python. MediaPipe ships wheels for a narrow set of Python versions, and if ComfyUI's embedded interpreter doesn't match your system one, the node loads fine but silently fails on the import. Use the same
pythonthat runs your ComfyUI.
The output shows up in the console too - you'll see Detected skin tone: MEDIUM in your terminal, which makes debugging trivial. It's not a tool for identity or recognition, and nobody's claiming it is; it answers exactly one question, in five emoji-shaped buckets, and for a tiny single-purpose node that's honestly fine.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| Skin Tone | STRING | — |