a is the alpha value for a grayscale color:
rgba(0,0,0,a)
I want to stack this color up to z times on a white rgb background.
Given z, how do I determine what a will create the widest gamut of resulting grayscale values?
I naively thought that I could solve with 255/z, but this results in my darkest grayscale values nowhere near 255. Here are three layers (z = 3), where I set a = 85. Alas! The darkest it gets is 180, so I could have increased a
and ended up with a wider gamut. But how?
https://codepen.io/jedierikb/pen/JjRebjB?editors=1010
const $c = document.getElementById( 'c' );
$c.width = 200;
$c.height = 200;
const ctx = $c.getContext( '2d' );
const numLayers = 3;
const alpha = Math.round(255/numLayers);
ctx.fillStyle = '#000000' + intToHex( alpha );
for (var i=0; i<numLayers; ++i) {
ctx.fillRect( i*10, i*10, 100, 100 );
}
//let's get a pixel
var id = ctx.getImageData(numLayers*10, numLayers*10, 1, 1).data;
console.log( 'for ' + numLayers + ' layers, using alpha ' + alpha );
console.log( 'resulting in darkest alpha being', id[3] );
In your example (both the image and the CodePen) the alpha is 0.33
.
The lightest color is 170
, which is 67%
of 255
. So the first layer of black removed a measure of brightness equal to its alpha (0.33 = 33%
).
Now, when the second layer is applied, the brigthness is substracted not from white but from the first layer. It had a brightness of 67%, so the second layer has a brightness of 67% * (1 - 0.33) = ~44%
, which in RGB is 255 * 44% = ~112
, equal to the color of the second layer.
The third layer will have a brightness of 44% * (1 - 0.33) = ~30%
, which in RGB is 255 * 30% = ~75
, equal to the color of the third layer.
Therefore, if you're applying black with alpha a
to a white background z
times, the z
-th color will be
255 * (1 - a)^z
The range of values can be calculated using:
range = 255 * (1 - a) - 255 * (1 - a)^z
first color - last color
By graphing this function you can tell that, for example, for z = 3
the local maximum is at 0.4226
. You can also solve the polynomial or just cheat, calculate the value for a
in increments of 0.05
and pick the highest result.
function greatestGamutAlpha(layers, precision = 5) {
let maxRangeAlpha,
maxRange = 0;
for (let a = 0; a < 100; a += precision) {
const alpha = a / 100,
range = 255 * (1 - alpha) - 255 * (1 - alpha) ** layers;
if (range > maxRange) {
maxRange = range;
maxRangeAlpha = alpha;
}
}
return maxRangeAlpha;
}