go-colorful, colour spaces, palettes and perceptual distance in Go
A library for playing with colors in go (golang).
At a glance
- What is it?
- go-colorful stores a colour in RGB and converts it between fifteen colour spaces, including the perceptually uniform Lab, Luv, HCL and Oklab, and it generates random palettes that are distinct by eye rather than by number. It was written because a game needed player colours that would not look alike, and because no colour space library existed for Go.
- Who is it for?
- go-colorful fits a Go program that has to pick colours that people will distinguish, whether that is player assignments, a chart palette or a theme, since the library gives the perceptual spaces needed to make that decision rather than leaving you in RGB.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 82 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Written because two players got the same colour
The origin story in the README is unusually specific and explains the design. The author makes games, and during the development of a game called Memory Which Does Not Suck the server had to assign players random colours, and sometimes two players got very similar colours, which was the problem. The same evening, a post on the Hacker News front page about picking colours by hue showed him how to do it properly, and the last observation is the one that turned it into a library, that there was no library for handling colour spaces available in Go. So the library does two things that belong together, it converts between colour spaces so that distance means something, and it generates colours that are distinct by eye. The description says it plainly, a library for playing with colors in Go, and the target audience is anyone who has discovered that two hex values that look different in the code can look identical on screen.
Fifteen colour spaces, and opinions about which to use
The library stores colours in RGB and provides conversions to and from a long list of spaces. RGB holds all three channels in the range zero to one. HSL is there for legacy reasons and the README asks you to forget it exists, and HSV is there too, with the advice that you are better off with HCL. The hex format is the internet colour format. Linear RGB is included for gamma correct rendering. The CIE family is the substantial part. CIE-XYZ is the standard space, CIE-xyY splits chromacity from luminance, and CIE-L*a*b* is perceptually uniform, so distances in it are meaningful. CIE-L*u*v* is similar to Lab and the README notes there is no consensus on which is better. CIE-L*C*h, which the code calls HCL, is described as generally the most useful one, Lab in polar coordinates, a better HSV. CIE LCh(uv) is called LuvLCh in code. HSLuv is offered as the better alternative to HSL. HPLuv is a variant of HSLuv whose valid colours are limited to pastels, which means a saturation value far above one indicates a colour that cannot be represented there. Oklab, by Björn Ottosson, improves on Lab with better perceptual uniformity for blue hues, and Oklch is its cylindrical form.
Reference white and the coordinates that leave the range
Two details separate this library from a set of naive conversion functions. The first is the reference white. For the spaces where it makes sense, namely XYZ, Lab, Luv and HCL, D65 is used as the reference white by default, and methods are provided for supplying your own. That matters because the choice of white shifts the result, and a pipeline that mixes a default with a custom one produces colours that do not agree with each other. The second is an unusual convention in the documentation, where a coordinate being almost in a range means that generally it is, but for very bright colours and depending on the reference white it might overflow slightly. The example given is the chroma of pure blue, #0000ff, which is 1.338 where the nominal range is almost zero to one. A program that clamps such a value will change the colour, and a program that assumes the range is hard will produce an invalid result. Reading this section is the difference between a conversion you can trust and one that is subtly wrong for saturated colours.
Which space to pick, and why distance matters
The README gives a rule of thumb that is worth more than the list of spaces, in the voice of the people behind I want hue. RGB fits how screens produce colour, CIE L*a*b* fits how humans perceive colour, and HCL fits how humans think about colours. The concrete recommendation follows from it. Whenever you would use HSV, go for CIE-L*C*h° instead, because for a fixed lightness L* and chroma C*, the hue angle rotates through colours of the same perceived brightness and intensity, which is exactly the property HSV lacks. The comparison section makes the same point from the other direction, noting that the Euclidean distance between colours in RGB does not correspond to visual or perceptual distance, so two pairs of colours at the same RGB distance can look nothing alike. The library's own file list backs this up, with a sort.go and its test, and the topic list names compare colors and palette as first class subjects rather than afterthoughts.
Installing it and building a colour in any space
Installation is a single go get, and the package is then imported like any other.
$ go get github.com/lucasb-eyer/go-colorfulimport "github.com/lucasb-eyer/go-colorful"A colour can be constructed from any of the supported spaces, and the README shows the same blue built several ways, which is a good way to see that the conversions agree.
// Any of the following should be the same
c := colorful.Color{0.313725, 0.478431, 0.721569}
c, err := colorful.Hex("#517AB8")
c = colorful.Hsv(216.0, 0.56, 0.722)
c = colorful.Xyz(0.189165, 0.190837, 0.480248)
c = colorful.Lab(0.507850, 0.040585,-0.370945)
c = colorful.Hcl(276.2440, 0.373160, 0.507849)The conversions back out are the mirror image, with Hex, Hsv, Xyz, Xyy, Lab, Luv, Hcl, OkLab and OkLch all available as methods. Note that the hex constructor returns an error, since a hex string can be malformed, while the numeric constructors cannot fail. The README also explains an API wart, that the functions relating to the xyY space are named oddly because Go requires an initial uppercase letter, and the author says plainly that he does not consider XyY a good name and invites an issue if you have a better suggestion.
Interop with color.Color, and the alpha trap
Because a colorful.Color implements Go's color.Color interface from the image/color package, it can be used anywhere that expects a color.Color, which is what makes it drop into existing code rather than sit beside it. The reverse conversion is available too, through the MakeColor function.
c, ok := colorful.MakeColor(color.Gray16{12345})The caveat here is the sharpest edge in the library and the README states it directly. The conversion using MakeColor hits a corner case when alpha is exactly zero, and because color.Color uses pre-multiplied alpha colours, the RGB values are lost, set to zero, and it is impossible to recover them. In that case MakeColor returns false as its second value, so the failure is detectable, but a caller that ignores the boolean will get black. The palette generators sit next to this in the tree, with happy_palettegen.go, soft_palettegen.go and warm_palettegen.go alongside colorgens.go and rand.go, and the stated use for them is generating random colours under constraints, such as colours of the same shade or shades of one colour, and gorgeous random palettes with distinct colours of the same temperature.
Versions, tests and the Go version floor
The library is not archived and the last push was on 2026-08-02, the same day as the v1.4.1 release, and before that v1.4.0 on 2026-03-28 and v1.3.0 on 2025-09-08, so the release cadence is steady. The README says the library supports Go 1.13 onwards, while the module file declares go 1.12, which is a small inconsistency that is easy to resolve in favour of the newer number. Unit tests are provided, and the file list shows that they are thorough rather than decorative, with test files covering the colour space conversions, the palette generators, the sorting and the hex colour handling. There is also an hsluv-snapshot-rev4.json in the tree, which is the snapshot data behind the HSLuv tests, a reminder that the colour space conversions are checked against a fixed reference rather than against themselves. The licence is MIT, the changelog is in the repository root, and the documentation lives in a doc/ directory beside the code.
Editorial conclusion
go-colorful fits a Go program that has to pick colours that people will distinguish, whether that is player assignments, a chart palette or a theme, since the library gives the perceptual spaces needed to make that decision rather than leaving you in RGB. It does not fit a project that only needs to parse a hex string, where the standard library is enough, and it does not fit a colour critical workflow that needs a specific reference white other than the default without reading the code for the methods that change it. Verify three things before you depend on it: which reference white your data assumes, since D65 is the default and the coordinates can sit slightly outside the nominal range for bright colours, whether the palette generators give you the kind of palette you need, and the Go version floor, since the README says Go 1.13 onwards while the module file declares go 1.12.
Frequently asked questions
What is go-colorful?
It is a Go library for playing with colors that stores colours in RGB and provides conversions to and from fifteen colour spaces. It supports Go 1.13 onwards, implements Go's color.Color interface, and includes generators for random palettes of distinct colours.
Which colour spaces does go-colorful support?
RGB, HSL, HSV, hex RGB, linear RGB, CIE-XYZ, CIE-xyY, CIE-L*a*b*, CIE-L*u*v*, CIE-L*C*h (HCL), CIE LCh(uv) called LuvLCh, HSLuv, HPLuv, Oklab and Oklch. D65 is the default reference white for the spaces where it applies, with methods to supply your own.
Why does the README recommend HCL over HSV?
Because for a fixed lightness L* and chroma C*, the hue angle in CIE-L*C*h° rotates through colours of the same perceived brightness and intensity, which HSV does not do. The README also notes that Euclidean distance in RGB does not correspond to perceptual distance.
What is the caveat when converting a color.Color into a colorful.Color?
MakeColor hits a corner case when alpha is exactly zero, because color.Color uses pre-multiplied alpha so the RGB values are lost and set to zero. In that case MakeColor returns false as its second return value.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/lucasb-eyer-go-colorful)Was this useful?