Skip to content

[css-color-4] Update Lab_to_XYZ sample code to avoid issues in implementations that use floats as internal storage. #14629

Closed

Description

@romainmenke

function Lab_to_XYZ(Lab) {
// Convert Lab to D50-adapted XYZ
// http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html
var κ = 24389/27; // 29^3/3^3
var ε = 216/24389; // 6^3/29^3
var f = [];
// compute f, starting with the luminance-related term
f[1] = (Lab[0] + 16)/116;
f[0] = Lab[1]/500 + f[1];
f[2] = f[1] - Lab[2]/200;
// compute xyz
var xyz = [
Math.pow(f[0],3) > ε ? Math.pow(f[0],3) : (116*f[0]-16)/κ,
Lab[0] > κ * ε ? Math.pow((Lab[0]+16)/116,3) : Lab[0]/κ,
Math.pow(f[2],3) > ε ? Math.pow(f[2],3) : (116*f[2]-16)/κ
];
// Compute XYZ by scaling xyz by reference white
return xyz.map((value, i) => value * D50[i]);
}

see: WebKit/WebKit#77513 (comment)

Upon review of the WebKit implementation I found that a significant number of issues could be traced back to this algorithm.

Might be better to already hint at this issue in the sample algorithm and to give a shape that better translates to implementations that use floats?

function Lab_to_XYZ(Lab) {
	// Convert Lab to D50-adapted XYZ
	// http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html
	var κ = 24389/27;   // 29^3/3^3
	var ε = 216/24389;  // 6^3/29^3
	var f = [];

	// Keep L out of L+16 for near-black precision
	var lightness116 = Lab[0]/116;
	var a500 = Lab[1]/500;
	var b200 = Lab[2]/200;

	// compute f, starting with the luminance-related term
	f[1] = lightness116 + (16/116);
	f[0] = f[1] + a500;
	f[2] = f[1] - b200;

	// compute xyz
	var xyz = [
		Math.pow(f[0],3) > ε ?   Math.pow(f[0],3)            : (lightness116 + a500) * (116/κ),
		Lab[0] > κ * ε ?         Math.pow((Lab[0]+16)/116,3) : Lab[0]/κ,
		Math.pow(f[2],3) > ε ?   Math.pow(f[2],3)            : (lightness116 - b200) * (116/κ)
	];

	// Compute XYZ by scaling xyz by reference white
	return xyz.map((value, i) => value * D50[i]);
}

@svgeesus ?

Activity

  1. changed the title [-][css-color-4] Update `Lab_to_XYZ` to avoid issues in implementations that use floats as internal storage.[/-] [+][css-color-4] Update `Lab_to_XYZ` sample code to avoid issues in implementations that use floats as internal storage.[/+] on Oct 19, 2026
  2. svgeesus commented on Oct 20, 2026

    @svgeesus
    Collaborator

    Good point: although the sample code is JavaScript, which uses F64 for the number type, it could be implemented in other languages and might use F32. This change is better with F32 and makes no difference for F64, so it is a good change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions