โ† Back to news

Show HN: Simple algorithm and color space to generate diverse skin tones

toneyalexander.github.io|223 points|58 comments|by automatoney|Aug 4, 2026

Creating a Versatile Color Space for Inclusive Skin Tones

Exploring the Spectrum of Human Diversity

๐Ÿ› ๏ธ Quick Start: The Implementation

If you are simply looking for the technical implementation, the author has provided a custom JavaScript-based color picker and a Python algorithm for procedural generation. The goal is to provide a mathematical foundation that developers can use to better depict the diversity of the human race.

Procedural Generation Logic (Python)

The following code demonstrates how to sample points from a sphere and map them into the RGB color space.

import math
from random import uniform

def select_point_deterministic(r_square: float = 2.) -> tuple[float, float, float]:
    """Uniformly sample from the sphere deterministically"""
    radius = r_square ** (1.0 / 2)
    phi = uniform(0, 2 * math.pi)
    costheta = uniform(-1, 1)
    n = uniform(0, 1)
    theta = math.acos(costheta)
    r = radius * (n ** (1.0 / 3))
    
    t = r * math.sin(theta) * math.cos(phi)
    u = r * math.sin(theta) * math.sin(phi)
    v = r * math.cos(theta)
    return (t, u, v)

def select_point_rejection(r_square: float = 2.) -> tuple[float, float, float]:
    """Uniformly sample from the sphere using rejection sampling"""
    radius = r_square ** (1.0 / 2)
    R = radius + 1
    while R > radius:
        t = uniform(-radius, radius)
        u = uniform(-radius, radius)
        v = uniform(-radius, radius)
        R = (t**2 + u**2 + v**2) ** (1.0 / 2)
    return (t, u, v)

def to_rgb(t, u, v) -> tuple[int, int, int]:
    # Transformation logic to map sphere coordinates to RGB
    x = (t - 0.15) / 0.45
    y = (v - 1.2 * t ** 2 + 0.2 * t + 0.655) / 1.84
    z = u / 3.6
    
    r = 28.77438370854 * x + 36.78307445559 * y - 19.69766918644 * z + 187.1436241611
    g = 35.38327306318 * x - 2.009931981182 * y + 47.93462563172 * z + 137.1073825503
    b = 36.14733717939 * x - 43.54346996173 * y - 28.50821294135 * z + 108.2241610738
    
    return int(r), int(g), int(b)

The Mathematical Transformation

The to_rgb function essentially performs a coordinate transformation. If we look at the logic as a set of equations, it follows this pattern:

x=tโˆ’0.150.45x = \frac{t - 0.15}{0.45} y=vโˆ’1.2t2+0.2t+0.6551.84y = \frac{v - 1.2t^2 + 0.2t + 0.655}{1.84} z=u3.6z = \frac{u}{3.6}

These values are then multiplied by specific weights to arrive at the final Red\text{Red}, Green\text{Green}, and Blue\text{Blue} channels.


๐ŸŒ Overview: What Colors Are We?

When asked to define the color of human skin, the simple answer is "brown," but the reality is infinitely more complex. Digitally representing the vast array of human skin tones is a significant challenge.

Too often, we see a limited palette presented as "inclusive," which inadvertently excludes large groups of people. The objective of this project is to find a "good enough" mathematical definition of the RGB space that encompasses plausible skin tones.

Note on "Good Enough": The author uses this term to emphasize that this is a starting point for developers, not an absolute scientific authority on human dermatology.

The Gap in Current Tools

Currently, we see a massive disparity in how skin tones are handled:

Tool/IndustryApproachResult
Emojisโ‰ˆ5\approx 5 shades (or yellow)Highly reductive
Makeup Brandsโ‰ˆ50\approx 50 shadesBetter, but still discrete
Character Creators16,777,21616,777,216 (Full RGB)Overwhelming/Unguided

Just picking 5 colors is not enough, but giving a user 16 million options without guidance is also inefficient. The goal is to find a middle ground.

  • Digital Art: Artists often use "Flesh Cloud" guides (like those by Tumblr user shiroxix) to find plausible tones.
  • Gaming: Modern games (e.g., Paralives) use a mix of presets and pickers, but the initial experience could be more intuitive.

โš ๏ธ Limitations and Constraints

It is vital to acknowledge that this model is a simplification. Real skin is not a single hex code.

๐Ÿงฌ Biological & Medical Complexity

  • Dynamic Nature: Skin color changes based on blood flow and melanin levels.
  • Light Interaction: Subsurface scattering of light through skin layers creates depth.
  • Natural Variance: Freckles, vitiligo, scarring, and hyperpigmentation mean one person has many colors.
  • Medical Conditions: Certain conditions push skin tones outside "plausible" ranges (e.g., Argyria causing blue-gray skin, or high bilirubin causing yellow/green tints).

๐Ÿ‘ค Subjectivity & Hardware

  1. Personal Bias: The creator is not a professional researcher; the results are based on subjective perception.
  2. Vision: While the author has no known color vision deficiency, subjectivity remains.
  3. Display Variance: RGB values are not universal. A color on an OLED screen looks different than on an LCD or under different ambient lighting.

๐Ÿ”ฌ Methodology

How were these numbers derived?

The process involved a manual labeling effort. The author identified a wide range of RGB colors that looked like plausible skin tones and used those data points to approximate the boundaries of the color space.

The Workflow Logic

This approach allows for the generation of diverse, realistic skin tones without requiring the user to navigate the entire RGB cube blindly.