Skip to content

Instantly share code, notes, and snippets.

@mnpenner
Last active April 22, 2024 02:41
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save mnpenner/70ab4f0836bbee548c71947021f93607 to your computer and use it in GitHub Desktop.
Save mnpenner/70ab4f0836bbee548c71947021f93607 to your computer and use it in GitHub Desktop.
RGB color hex to lightness
// based on https://stackoverflow.com/a/56678483/65387
type RGB = [r: number, g: number, b: number]
const UNK = 255 / 2
/**
* @param hex RGB hex string like "#CCCFDB"
* @returns RGB tuple in [0-255]
*/
function hexToRgb(hex: string): RGB {
const h = String(hex).replace(/^#/, '')
if(h.length === 3) {
return [
parseRadix(h[0] + h[0], 16, UNK),
parseRadix(h[1] + h[1], 16, UNK),
parseRadix(h[2] + h[2], 16, UNK),
]
}
if(h.length === 6) {
return [
parseRadix(h.slice(0, 2), 16, UNK),
parseRadix(h.slice(2, 4), 16, UNK),
parseRadix(h.slice(4, 6), 16, UNK),
]
}
return [UNK, UNK, UNK]
}
function sRGBtoLin(colorChannel: number) {
// Send this function a decimal sRGB gamma encoded color value
// between 0.0 and 1.0, and it returns a linearized value.
if(colorChannel <= 0.04045) {
return colorChannel / 12.92
}
return Math.pow((colorChannel + 0.055) / 1.055, 2.4)
}
/**
* @param r Red, [0-1]
* @param g Green, [0-1]
* @param b Blue, [0-1]
* @returns Luminance, [0-1]
*/
function rgbToY(r: number, g: number, b: number) {
return 0.2126 * sRGBtoLin(r) + 0.7152 * sRGBtoLin(g) + 0.0722 * sRGBtoLin(b)
}
/**
* Luminance to perceived lightness.
*
* @param Y Luminance, [0-1]
*/
function YtoLstar(Y: number) {
// Send this function a luminance value between 0.0 and 1.0,
// and it returns L* which is "perceptual lightness"
if(Y <= (216 / 24389)) { // The CIE standard states 0.008856 but 216/24389 is the intent for 0.008856451679036
return Y * (24389 / 27) // The CIE standard states 903.3, but 24389/27 is the intent, making 903.296296296296296
}
return Math.pow(Y, (1 / 3)) * 116 - 16
}
/**
* Calculate perceived lightness from RGB hex string.
*
* @param rgb RGB hex string like "#CCCFDB"
* @returns Lightness value, [0-100].
*/
export function rgbHexToLightness(rgb: string) {
const [r, g, b] = hexToRgb(rgb)
return YtoLstar(rgbToY(r / 255, g / 255, b / 255))
}
@Myndex
Copy link

Myndex commented Oct 16, 2022

Nice!

You might be interested in my contrast extension for L*, the DeltaPhiStar Method which is a general purpose contrast metric for high spatial frequency stimuli (i.e. text against a background).

    deltaPhiStar = (Math.abs(bgLstar ** 1.618 - txLstar ** 1.618) ** 0.618) * 1.414 - 40 ;
    
    // a**b is equiv. to Math.pow(a,b)

deltaPhiStar is part of the research for APCA so the scaling of * 1.414 - 40 is designed to roughly align deltaPhiStar with APCA Lc values.

Thank you for reading,

Andy

Andrew Somers
Color Scientist @ Myndex Research
Inventor of APCA, deltaPhiStar, SACAM

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