LCH 🠖 RGB mismatch compared to other libraries
This library looks wonderfully feature rich, but playing with the demo page I noticed that it produces a different RGB color than other libraries, given LCH input.
Input: { l: 59.6, c: 61.3, h: 24.3 }
Output:
- css.land/lch:
#ec5f67 - GIMP:
#ec5f67 - colord:
#ec5f67 - d3-color:
#ec5f67 - chroma.js:
#ef6066
Is this due to differences in the transformation stages, or maybe LCHab vs LCHuv? I have very limited knowledge of this topic, so I couldn't say.
I noticed this as well when converting from RGB to LCH.
Input: #421AFD
Output:
- css.land/lch:
lch(33.789% 122.345 301.987) - chroma.js:
lch(35.959% 125.517 306.756)
Most noticeable is the +5 to the hue, which skews the colour closer to purple
as far as I understand it. the conversion depends on the white-point.
CIELAB is calculated relative to a reference white, for which the CIE recommends the use of CIE Standard Illuminant D65.[1] D65 is used in the vast majority industries and applications, with the notable exception being the printing industry which uses D50
https://en.wikipedia.org/wiki/CIELAB_color_space#Cylindrical_representation:_CIELCh_or_CIEHLC
So you would have to know what white-point each library is using to be able to compare its results.
It looks like colord uses W3C sample code for making the conversion from RGB → LAB: https://github.com/omgovich/colord/blob/master/src/colorModels/lab.ts#L45 https://www.w3.org/TR/css-color-4/#color-conversion-code
The W3C draft specifies the D50 whitepoint:
Convert from a D65 whitepoint (used by sRGB) to the D50 whitepoint used in Lab
https://www.w3.org/TR/css-color-4/#rgb-to-lab
d3-color uses their own algorithm and also uses D50 as the whitepoint: https://github.com/d3/d3-color/blob/main/src/lab.js#L5 https://observablehq.com/@mbostock/lab-and-rgb
And chroma.js uses D65 here: https://github.com/gka/chroma.js/blob/master/src/io/lab/lab-constants.js#L6
So this looks to be the reason for the difference
https://css.land/lch seems to use https://drafts.csswg.org/css-color-4/utilities.js for converting between sRGB and LCH, which document the steps like this:
function sRGB_to_LCH(RGB) {
// convert an array of gamma-corrected sRGB values
// in the 0.0 to 1.0 range
// to linear-light sRGB, then to CIE XYZ,
// then adapt from D65 to D50,
// then convert XYZ to CIE Lab
// and finally, convert to CIE LCH
function LCH_to_sRGB(LCH) {
// convert an array of CIE LCH values
// to CIE Lab, and then to XYZ,
// adapt from D50 to D65,
// then convert XYZ to linear-light sRGB
// and finally to gamma corrected sRGB
// for in-gamut colors, components are in the 0.0 to 1.0 range
// out of gamut colors may have negative components
// or components greater than 1.0
// so check for that :)
I do not know what steps chroma.js uses, but it would be interesting to know and hear the reasons for the difference in steps taken.