Reputation: 796
I am building a free real color mixer for 3 colors. To feel real I first convert hex or rgb to hsl and calculate with h, s, l values. I figured out to mix 2 colors:
function hslMixer2c(hsl10,hsl11,hsl12,hsl20,hsl21,hsl22,amount1,amount2) { // read hsl10 as hsl1[0], 1st value from hsl string
var amountTot = amount1 + amount2;
if (Math.abs(hsl10 - hsl20) > 0.5) { hsl10 += 1; } // > 179.5 is shorter part from wheel to 359
var h = (amount1 / amountTot) * hsl10 + (amount2 / amountTot) * hsl20;
var s = (amount1 / amountTot) * hsl11 + (amount2 / amountTot) * hsl21;
var l = (amount1 / amountTot) * hsl12 + (amount2 / amountTot) * hsl22;
if (h > 1) { h -= 1; }
return [h, s, l];
}
So far, so good...
But I want to add a 3th color to mix. I tried several calculations but the results are different when changing the order of the used colors.
function hslMixer3c(hsl10,hsl11,hsl12,hsl20,hsl21,hsl22,hsl30,hsl31,hsl32,amount1,amount2,amount3) { // read hsl10 as hsl1[0], 1st value from hsl string
var amountTot = amount1 + amount2 + amount3;
if (Math.abs(hsl10 - hsl20) > 0.5) { hsl10 += 1; } // > 179.5 is andere kant naar 359 korter)
var hsl90 = (amount1 / amountTot) * hsl10 + (amount2 / amountTot) * hsl20; // hsl9x is sub mix
var hsl91 = (amount1 / amountTot) * hsl11 + (amount2 / amountTot) * hsl21;
var hsl92 = (amount1 / amountTot) * hsl12 + (amount2 / amountTot) * hsl22;
if (hsl90 > 1) { hsl90 -= 1; }
if (Math.abs(hsl90 - hsl30) > 0.5) { hsl90 += 1; } // > 179.5 is andere kant naar 359 korter)
var h = hsl90 + (amount3 / amountTot) * hsl30;
var s = hsl91 + (amount3 / amountTot) * hsl31;
var l = hsl92 + (amount3 / amountTot) * hsl32;
if (h > 1) { h -= 1; }
return [h, s, l];
}
Set color 1st at 90, 2nd at 180, 3rd at 300 the calculation is first for color 1 and 2 and additional 3 gives 190. But when I take 3 and 1 as first, the color goes to the upper side of the wheel and gives with addition color 3 a whole other result off course.
Can anyone help me pointing to the right direction, maybe with a sample?
Upvotes: 3
Views: 3630
Reputation: 22769
One approach might be to convert each of the colors hue angles into a vector format:
x = Math.cos(hue / 180 * Math.PI) * saturation
y = Math.sin(hue / 180 * Math.PI) * saturation
z = lightness
and then simply sum up these vectors (axis by axis) and divide by the number of colors.
If you would like some colors to contribute more towards the final color than others you could multiply each vector by a weight (where the sums of all weights = 1) prior to adding the vectors.
Finally convert back to hue and saturation:
h = Math.atan2(y, x) * 180 / Math.PI
s = Math.sqrt(x * x + y * y)
l = z
Upvotes: 6