The XMPP site has a document describing a process for deterministically mapping strings to colors with [XEP-0392](https://xmpp.org/extensions/xep-0392.html#algorithm-genpalette). I've had this document saved in my notes for two years now and I haven't really had a good excuse to apply it until now.
I won't get too detailed here but the short version is
- I've got a spreadsheet of addresses.
- Each entry has a category and I'm turning that spreadsheet into a KML document.
- I want each KML placemark to be colored according to its category but I don't want to manually assign colors to categories. I don't have much of an eye for design and categories might change over time.
All of this adds up to finding a way to programmatically turn a string into a color.
I made an attempt at implementing XEP-0392 verbatim but I got walled by two details:
1. Go doesn't have any HSL handling in the standard library.
2. I don't understand HSLuv well enough to parse the instructions for converting the SHA1 hash to a color. HSL is comprised of 3 components ( hue, saturation, and lightness ) but the XEP-0392 document only talks about generating an angle.
I do understand RGB and how hashes work so I was able to synthesize that into a close-enough solution:
`ToRGB` is pretty straightforward, implementing the algorithm described in the previous section. We take the SHA1 of the string, break it into chunks, convert those into `uint64`s to account for the 7 bytes ( 56 bits total ) we're turning into an integer. The blue value overlaps the green by a few bits but that this isn't a security system so that doesn't matter much.
`ToRGBA` functions almost identically, with a few small changes. Instead of 7 byte chunks, we use 5 byte chunks and adjust the `Scale` call accordingly for the 40 bit chunks.
The final bit of magic we need is a way to scale these 64 bit integers down to between 0 and 255 to represent the RGB ( and sometimes A ) values that make up the `image.Color`. Enter the `Scale` function:
This does a bit of relatively straightforward math to smoothly scale the input into the desired range. Not a lot to say about it, it's just math and I don't know much about where it came from. I've had this floating around in various repos for years and I don't have any notes on where I found it.
There's a few places where this code could be meaningfully improved.
- It'd be nice to have HSL/HSV colors as an option. I've personally never been in a situation where RGB wasn't enough to do what I needed, but they exist for a reason. [HSLuv](https://www.hsluv.org/) claims to better capture colors as perceived by the human eye and that sounds pretty neat.
- Accessibility and consistency. Sometimes the color for a word will be almost identical to its background making portions of the text hard or impossible to read. It'd be cool to be able to limit color generation to values within certain ranges of a color palette. This is probably doable with another application of `Scale`.