How to automatically generate N "distinct" colors? How to automatically generate N "distinct" colors? java java

How to automatically generate N "distinct" colors?


This questions appears in quite a few SO discussions:

Different solutions are proposed, but none are optimal. Luckily, science comes to the rescue

Arbitrary N

The last 2 will be free via most university libraries / proxies.

N is finite and relatively small

In this case, one could go for a list solution. A very interesting article in the subject is freely available:

There are several color lists to consider:

  • Boynton's list of 11 colors that are almost never confused (available in the first paper of the previous section)
  • Kelly's 22 colors of maximum contrast (available in the paper above)

I also ran into this Palette by an MIT student.Lastly, The following links may be useful in converting between different color systems / coordinates (some colors in the articles are not specified in RGB, for instance):

For Kelly's and Boynton's list, I've already made the conversion to RGB (with the exception of white and black, which should be obvious). Some C# code:

public static ReadOnlyCollection<Color> KellysMaxContrastSet{    get { return _kellysMaxContrastSet.AsReadOnly(); }}private static readonly List<Color> _kellysMaxContrastSet = new List<Color>{    UIntToColor(0xFFFFB300), //Vivid Yellow    UIntToColor(0xFF803E75), //Strong Purple    UIntToColor(0xFFFF6800), //Vivid Orange    UIntToColor(0xFFA6BDD7), //Very Light Blue    UIntToColor(0xFFC10020), //Vivid Red    UIntToColor(0xFFCEA262), //Grayish Yellow    UIntToColor(0xFF817066), //Medium Gray    //The following will not be good for people with defective color vision    UIntToColor(0xFF007D34), //Vivid Green    UIntToColor(0xFFF6768E), //Strong Purplish Pink    UIntToColor(0xFF00538A), //Strong Blue    UIntToColor(0xFFFF7A5C), //Strong Yellowish Pink    UIntToColor(0xFF53377A), //Strong Violet    UIntToColor(0xFFFF8E00), //Vivid Orange Yellow    UIntToColor(0xFFB32851), //Strong Purplish Red    UIntToColor(0xFFF4C800), //Vivid Greenish Yellow    UIntToColor(0xFF7F180D), //Strong Reddish Brown    UIntToColor(0xFF93AA00), //Vivid Yellowish Green    UIntToColor(0xFF593315), //Deep Yellowish Brown    UIntToColor(0xFFF13A13), //Vivid Reddish Orange    UIntToColor(0xFF232C16), //Dark Olive Green};public static ReadOnlyCollection<Color> BoyntonOptimized{    get { return _boyntonOptimized.AsReadOnly(); }}private static readonly List<Color> _boyntonOptimized = new List<Color>{    Color.FromArgb(0, 0, 255),      //Blue    Color.FromArgb(255, 0, 0),      //Red    Color.FromArgb(0, 255, 0),      //Green    Color.FromArgb(255, 255, 0),    //Yellow    Color.FromArgb(255, 0, 255),    //Magenta    Color.FromArgb(255, 128, 128),  //Pink    Color.FromArgb(128, 128, 128),  //Gray    Color.FromArgb(128, 0, 0),      //Brown    Color.FromArgb(255, 128, 0),    //Orange};static public Color UIntToColor(uint color){    var a = (byte)(color >> 24);    var r = (byte)(color >> 16);    var g = (byte)(color >> 8);    var b = (byte)(color >> 0);    return Color.FromArgb(a, r, g, b);}

And here are the RGB values in hex and 8-bit-per-channel representations:

kelly_colors_hex = [    0xFFB300, # Vivid Yellow    0x803E75, # Strong Purple    0xFF6800, # Vivid Orange    0xA6BDD7, # Very Light Blue    0xC10020, # Vivid Red    0xCEA262, # Grayish Yellow    0x817066, # Medium Gray    # The following don't work well for people with defective color vision    0x007D34, # Vivid Green    0xF6768E, # Strong Purplish Pink    0x00538A, # Strong Blue    0xFF7A5C, # Strong Yellowish Pink    0x53377A, # Strong Violet    0xFF8E00, # Vivid Orange Yellow    0xB32851, # Strong Purplish Red    0xF4C800, # Vivid Greenish Yellow    0x7F180D, # Strong Reddish Brown    0x93AA00, # Vivid Yellowish Green    0x593315, # Deep Yellowish Brown    0xF13A13, # Vivid Reddish Orange    0x232C16, # Dark Olive Green    ]kelly_colors = dict(vivid_yellow=(255, 179, 0),                    strong_purple=(128, 62, 117),                    vivid_orange=(255, 104, 0),                    very_light_blue=(166, 189, 215),                    vivid_red=(193, 0, 32),                    grayish_yellow=(206, 162, 98),                    medium_gray=(129, 112, 102),                    # these aren't good for people with defective color vision:                    vivid_green=(0, 125, 52),                    strong_purplish_pink=(246, 118, 142),                    strong_blue=(0, 83, 138),                    strong_yellowish_pink=(255, 122, 92),                    strong_violet=(83, 55, 122),                    vivid_orange_yellow=(255, 142, 0),                    strong_purplish_red=(179, 40, 81),                    vivid_greenish_yellow=(244, 200, 0),                    strong_reddish_brown=(127, 24, 13),                    vivid_yellowish_green=(147, 170, 0),                    deep_yellowish_brown=(89, 51, 21),                    vivid_reddish_orange=(241, 58, 19),                    dark_olive_green=(35, 44, 22))

For all you Java developers, here are the JavaFX colors:

// Don't forget to import javafx.scene.paint.Color;private static final Color[] KELLY_COLORS = {    Color.web("0xFFB300"),    // Vivid Yellow    Color.web("0x803E75"),    // Strong Purple    Color.web("0xFF6800"),    // Vivid Orange    Color.web("0xA6BDD7"),    // Very Light Blue    Color.web("0xC10020"),    // Vivid Red    Color.web("0xCEA262"),    // Grayish Yellow    Color.web("0x817066"),    // Medium Gray    Color.web("0x007D34"),    // Vivid Green    Color.web("0xF6768E"),    // Strong Purplish Pink    Color.web("0x00538A"),    // Strong Blue    Color.web("0xFF7A5C"),    // Strong Yellowish Pink    Color.web("0x53377A"),    // Strong Violet    Color.web("0xFF8E00"),    // Vivid Orange Yellow    Color.web("0xB32851"),    // Strong Purplish Red    Color.web("0xF4C800"),    // Vivid Greenish Yellow    Color.web("0x7F180D"),    // Strong Reddish Brown    Color.web("0x93AA00"),    // Vivid Yellowish Green    Color.web("0x593315"),    // Deep Yellowish Brown    Color.web("0xF13A13"),    // Vivid Reddish Orange    Color.web("0x232C16"),    // Dark Olive Green};

the following is the unsorted kelly colors according to the order above.

unsorted kelly colors

the following is the sorted kelly colors according to hues (note that some yellows are not very contrasting)

 sorted kelly colors


You can use the HSL color model to create your colors.

If all you want is differing hues (likely), and slight variations on lightness or saturation, you can distribute the hues like so:

// assumes hue [0, 360), saturation [0, 100), lightness [0, 100)for(i = 0; i < 360; i += 360 / num_colors) {    HSLColor c;    c.hue = i;    c.saturation = 90 + randf() * 10;    c.lightness = 50 + randf() * 10;    addColor(c);}


Like Uri Cohen's answer, but is a generator instead. Will start by using colors far apart. Deterministic.

Sample, left colors first:sample

#!/usr/bin/env python3from typing import Iterable, Tupleimport colorsysimport itertoolsfrom fractions import Fractionfrom pprint import pprintdef zenos_dichotomy() -> Iterable[Fraction]:    """    http://en.wikipedia.org/wiki/1/2_%2B_1/4_%2B_1/8_%2B_1/16_%2B_%C2%B7_%C2%B7_%C2%B7    """    for k in itertools.count():        yield Fraction(1,2**k)def fracs() -> Iterable[Fraction]:    """    [Fraction(0, 1), Fraction(1, 2), Fraction(1, 4), Fraction(3, 4), Fraction(1, 8), Fraction(3, 8), Fraction(5, 8), Fraction(7, 8), Fraction(1, 16), Fraction(3, 16), ...]    [0.0, 0.5, 0.25, 0.75, 0.125, 0.375, 0.625, 0.875, 0.0625, 0.1875, ...]    """    yield Fraction(0)    for k in zenos_dichotomy():        i = k.denominator # [1,2,4,8,16,...]        for j in range(1,i,2):            yield Fraction(j,i)# can be used for the v in hsv to map linear values 0..1 to something that looks equidistant# bias = lambda x: (math.sqrt(x/3)/Fraction(2,3)+Fraction(1,3))/Fraction(6,5)HSVTuple = Tuple[Fraction, Fraction, Fraction]RGBTuple = Tuple[float, float, float]def hue_to_tones(h: Fraction) -> Iterable[HSVTuple]:    for s in [Fraction(6,10)]: # optionally use range        for v in [Fraction(8,10),Fraction(5,10)]: # could use range too            yield (h, s, v) # use bias for v here if you use rangedef hsv_to_rgb(x: HSVTuple) -> RGBTuple:    return colorsys.hsv_to_rgb(*map(float, x))flatten = itertools.chain.from_iterabledef hsvs() -> Iterable[HSVTuple]:    return flatten(map(hue_to_tones, fracs()))def rgbs() -> Iterable[RGBTuple]:    return map(hsv_to_rgb, hsvs())def rgb_to_css(x: RGBTuple) -> str:    uint8tuple = map(lambda y: int(y*255), x)    return "rgb({},{},{})".format(*uint8tuple)def css_colors() -> Iterable[str]:    return map(rgb_to_css, rgbs())if __name__ == "__main__":    # sample 100 colors in css format    sample_colors = list(itertools.islice(css_colors(), 100))    pprint(sample_colors)