I’m drawing stuff over a background image of the ocean, which looks bluish-green. Naturally, bluish-greens and some grays don’t have enough contrast to stand out, so they kind of get lost in the image.

I initially tried calculating luminance from RGB triplets, which does work for readability, but I was using the web readability thresholds for the luminance ratio (4.5 for normal, 7 for high contrast), and it didn’t work very well because my background is sort of middling in luminance, so it punched out most of the middle range of colors, resulting in everything being either dark and similar-looking, or light and similar-looking.

I switched to RGB color distance (which, yes, isn’t perceptually flat, but this isn’t really a color-sensitive application, beyond not making them too similar). In order to figure out where my threshold should be, I wanted to get a list of all the wxPython named colors, and what they looked like, and their distance from “cadetblue”, which is about the same color as the average of my ocean background.

#!/usr/bin/python # Generate a page that previews all the colors from WX
import wx app = wx.App(False) import wx.lib.colourdb as wxColorDB def get_d(colorname1, colorname2="cadetblue"): target_color = wx.Colour(colorname1).Get() source_color = wx.Colour(colorname2).Get() # Euclidian distance d = math.sqrt(sum([pow(x[0] - x[1], 2) for x in zip(target_color[:3], source_color)])) return d print("<html>")
print(" <body>") colors = list(set(wxColorDB.getColourList()))
colors.sort(key=lambda c: get_d(c, "white")) for color in colors: cRGB = wx.Colour(color).Get() print(" <div style=\"white-space: nowrap\">") print(f" <div style=\"display: inline-block; width:300px\">{color}</div>") print(f" <div style=\"display: inline-block;height:30px;overflow:auto;background-color:rgb({cRGB[0]},{cRGB[1]},{cRGB[2]}); width:100px\"> </div>") d = get_d(color, "white") print(f" <div style=\"display: inline-block\">{d}</div>") print(" </div>") print(" </body>")

That script generates an HTML page with the colors in it, ranked by distance from the given color name. I picked white for the version published here, but as you can see, the default is “cadetblue”. If you pick a color name WX doesn’t know, you’re going to have a bad time.

A distance of 80 seemed to work pretty well for me, so as a rule of thumb, 80 units of color distance gets you a distinct color in 8-bit-per-color RGB color space.

There are, of course, some problems to be aware of. For instance, distance folds hue and value together, so getting brighter or darker and remaining the same hue can make up a lot of that 80 units, without necessarily getting good contrast.