Mapping Pixel Art Palettes
While researching pixel art palettes, one of the things I noticed was a lot of discussion about color ramps. As I read it, these are essentially subsets of the colors in the palette that form distinct gradients and they are most commonly shown laid out on a grid interlocking with each other like a crossword puzzle. This seemed like a nice, qualitative way to assess a palette and got me wondering how to map out a palette automatically. I think I’ve come up with an interesting alternative to these crossword-style layouts.
Initially, I was focused on producing the crossword-style visualizations. But there are two main issues with doing this. First, how do you identify plausible ramps? One early approach that I tried was to interpolate between each pair of colors using the LAB color space and look for where the line passes close to other colors in the palette. Unfortunately, with small palettes this tends not to find many long ramps. In practice, ramps seem to curve and meander a bit through the color space. Secondly, even if you do find a good set of ramps, how do you lay them out nicely on a square grid? If one color is shared by more than two ramps, there’s simply no real way to show this without repeating the color elsewhere.
The alternative approach that I came up with is rather simple by comparison: just draw an arrow from each color to all of the colors that are both lighter than it and within a certain color distance of it. I use the L of the color in the LAB color space for the first criteria, and the CIE DE 2000 color difference metric for the second criteria (in reality, any lightness and color difference metric should do – I just happened to have these already implemented). This forms a directed acyclic graph which the GraphViz “dot” program can do an excellent job of arranging.
What should be the threshold for whether two colors are close enough to have an edge between them here? The heuristic that I use here to is to first compute the minimum spanning tree of all the colors using the CIE DE 2000 values for the edge weights, and then use the heaviest edge weight in that tree as the threshold for building the graph. This is the smallest threshold that ensures that the result is a single-component graph.
So here are some examples of the results that I got from doing this on a few 16-color palettes. The first one is from the aek-16 palette, a set of colors chosen by the program described in my previous post:
The neat thing is that even though the “dot” program knows nothing of color hue, lightness, or saturation, it still seems to do a pretty reasonable job of organizing the colors together into a logical arrangement based only on the constraints imposed by which nodes share edges. You can see some plausible color ramps by following the arrows.
Another neat thing that I found is that by virtue of the optimization criterion used to produce this palette in the first place, each pair of colors connected by an arrow has very nearly the same color difference from each other as any other pair. In other words, the steps here are computed to be perceptually uniform.
Moving on, here is the 16-color palette by Arne Niklas Jansson:
Then a pair of palettes from Richard “DawnBringer” Fhager. The first one is the popular DB16 palette:
The second is his later Fun16 palette:
Next up is the palette for the PICO-8 fantasy console:
Finally, I also tried a pair of palettes from real world hardware. First, the Commodore 64 palette:
Secondly, the default EGA palette:
There are relatively fewer 32-color palettes out there, but I did map a few. As before, I’ll start with my own. Here’s my aek-32 palette. Like its 16-color companion, this palette also has nearly even color difference steps between each pair of colors:
Next, here’s Arne Jansson’s extended palette, adding 16 additional colors to his previous 16:
And finally, here’s DawnBringer’s DB32:
Lastly, just to try it on a slightly larger palette here’s what I got for the colors available on the NES console: