let names = ["RGB mean", "RGB root mean²", "LAB mean"];
let analyzers = [simpleAverage,squaredAverage,labAverage];
let elapsed = [0,0,0];
let c1,c2,r,d1,d2,sp,sp2
for(let i=0;i<50000;i++){
c1 = i==0 ? [255,0,0]: i==1 ? [0,255,0] : i==2 ? [0,0,255] : i==3 ? [255,255,0]: i==4 ? [255,0,255] : i==5 ? [0,255,255] : randRGB()
c2 = i==0 ? [0,0,255]: i==1 ? [255,0,0] : i==2 ? [0,255,0] : i==3 ? [0,255,255]: i==4 ? [255,255,0] : i==5 ? [255,0,255]: randRGB()
if(i<100) {
r = document.createElement("div")
r.classList.add("row")
d1 = document.createElement("div")
d2 = document.createElement("div")
d1.style.backgroundColor = rgbStr(c1)
sp = document.createElement("span")
sp.style.color = d1.style.backgroundColor
sp.innerText = `Color 1: ${rgbStr(c1)}`
d1.appendChild(sp)
d2.style.backgroundColor = rgbStr(c2)
sp2 = document.createElement("span")
sp2.style.color = d2.style.backgroundColor
sp2.innerText = `Color 2: ${rgbStr(c2)}`
d2.appendChild(sp2)
r.appendChild(d1)
r.appendChild(d2)
}
for(let k=0;k<3;k++){
const t0 = performance.now()
let ave = analyzers[k](c1,c2)
elapsed[k] += performance.now()-t0;
if(i<100){
let d = document.createElement("div")
d.style.backgroundColor = rgbStr(ave)
let sp = document.createElement("span")
sp.style.color = d.style.backgroundColor
sp.innerText = `${names[k]}: ${d.style.backgroundColor}`
d.appendChild(sp)
r.appendChild(d)
}
}
if(i<100) colorResults.appendChild(r)
}
elapsed.forEach((ms,i)=>{
timingResults.insertAdjacentHTML("beforeEnd", `<div>${names[i]}: ${ms.toFixed(2)}ms</div>`)
})
function rgbStr(c){
return `rgb(${parseInt(c[0])},${parseInt(c[1])},${parseInt(c[2])})`
}
function labAverage(c1,c2){
let l1 = XYZtoLAB(RGBtoXYZ(c1))
let l2 = XYZtoLAB(RGBtoXYZ(c2))
let ave = simpleAverage(l1,l2)
let newXYZ = LabToXYZ(ave)
let newRGB = XYZtoRGB(newXYZ)
return newRGB
}
function simpleAverage(c1,c2) {
return [((c1[0]+c2[0])/2), ((c1[1]+c2[1])/2), ((c2[2]+c1[2])/2)]
}
function squaredAverage(c1,c2) {
return [
Math.floor(Math.sqrt((c1[0]**2+c2[0]**2)/2)),
Math.floor(Math.sqrt((c1[1]**2+c2[1]**2)/2)),
Math.floor(Math.sqrt((c2[2]**2+c1[2]**2)/2))
];
}
function randRGB(){
return [randInt(256),randInt(256), randInt(256)]
}
function randInt(max) { return Math.floor(Math.random()*max)}
function RGBtoXYZ(RGB) {
//https://stackoverflow.com/questions/15408522/rgb-to-xyz-and-lab-colours-conversion
let R = RGB[0];
let G = RGB[1];
let B = RGB[2];
var_R = parseFloat(R / 255) //R from 0 to 255
var_G = parseFloat(G / 255) //G from 0 to 255
var_B = parseFloat(B / 255) //B from 0 to 255
if (var_R > 0.04045) var_R = Math.pow((var_R + 0.055) / 1.055, 2.4)
else var_R = var_R / 12.92
if (var_G > 0.04045) var_G = Math.pow((var_G + 0.055) / 1.055, 2.4)
else var_G = var_G / 12.92
if (var_B > 0.04045) var_B = Math.pow((var_B + 0.055) / 1.055, 2.4)
else var_B = var_B / 12.92
var_R = var_R * 100
var_G = var_G * 100
var_B = var_B * 100
//Observer. = 2°, Illuminant = D65
X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805
Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722
Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505
return [X, Y, Z]
}
function XYZtoLAB(XYZ) {
//https://stackoverflow.com/questions/15408522/rgb-to-xyz-and-lab-colours-conversion
let x = XYZ[0];
let y = XYZ[1];
let z = XYZ[2];
var ref_X = 95.047;
var ref_Y = 100.000;
var ref_Z = 108.883;
var_X = x / ref_X //ref_X = 95.047 Observer= 2°, Illuminant= D65
var_Y = y / ref_Y //ref_Y = 100.000
var_Z = z / ref_Z //ref_Z = 108.883
if (var_X > 0.008856) var_X = Math.pow(var_X, (1 / 3))
else var_X = (7.787 * var_X) + (16 / 116)
if (var_Y > 0.008856) var_Y = Math.pow(var_Y, (1 / 3))
else var_Y = (7.787 * var_Y) + (16 / 116)
if (var_Z > 0.008856) var_Z = Math.pow(var_Z, (1 / 3))
else var_Z = (7.787 * var_Z) + (16 / 116)
CIE_L = (116 * var_Y) - 16
CIE_a = 500 * (var_X - var_Y)
CIE_b = 200 * (var_Y - var_Z)
return [CIE_L, CIE_a, CIE_b]
}
function LabToXYZ(lab){
//adapted from easyRGB.com
//The tristimulus values are (X, Y, Z) = (109.85, 100.00, 35.58)
var ref_X = 95.047;
var ref_Y = 100.000;
var ref_Z = 108.883;
let l = lab[0];
let a = lab[1];
let b = lab[2];
let var_Y = ( l + 16 ) / 116
let var_X = a / 500 + var_Y
let var_Z = var_Y - b / 200
if ( var_Y**3 > 0.008856 ) var_Y = var_Y**3
else var_Y = ( var_Y - 16 / 116 ) / 7.787
if ( var_X**3 > 0.008856 ) var_X = var_X**3
else var_X = ( var_X - 16 / 116 ) / 7.787
if ( var_Z**3 > 0.008856 ) var_Z = var_Z**3
else var_Z = ( var_Z - 16 / 116 ) / 7.787
X = var_X * ref_X
Y = var_Y * ref_Y
Z = var_Z * ref_Z
return [X, Y, Z];
}
function XYZtoRGB(xyz) {
//adapted from easyRGB.com
let X = xyz[0];
let Y = xyz[1];
let Z = xyz[2];
var_X = X / 100
var_Y = Y / 100
var_Z = Z / 100
var_R = var_X * 3.2406 + var_Y * -1.5372 + var_Z * -0.4986
var_G = var_X * -0.9689 + var_Y * 1.8758 + var_Z * 0.0415
var_B = var_X * 0.0557 + var_Y * -0.2040 + var_Z * 1.0570
if ( var_R > 0.0031308 ) var_R = 1.055 * ( var_R**( 1 / 2.4 ) ) - 0.055
else var_R = 12.92 * var_R
if ( var_G > 0.0031308 ) var_G = 1.055 * ( var_G**( 1 / 2.4 ) ) - 0.055
else var_G = 12.92 * var_G
if ( var_B > 0.0031308 ) var_B = 1.055 * ( var_B**( 1 / 2.4 ) ) - 0.055
else var_B = 12.92 * var_B
sR = var_R * 255
sG = var_G * 255
sB = var_B * 255
return [sR,sG,sB];
}
body {
font-size:1.3em;
background-color:#f5f5f5;
}
span {
filter: invert(100%) grayscale(100%) contrast(1000);
}
.row {
margin:20px;
}
.row div:nth-child(1), .row div:nth-child(2) {
padding:7px;
text-align:center;
}
.row > div {
margin:1px;
}
50000 iterations (showing first 100 results):
<div id="timingResults"></div>
<div id="colorResults"></div>