I've searched Google with no avail.
If I have the following color rgb(50,100,200). I can calculate the HSV saturation with: (200-50)/200, which is 0.75. If the want the color shade with a different saturation, how can I calculate that?
I'm using a color picker to get these test data:
saturation 1.00 = r:0 g:66 b:200
saturation 0.75 = r:50 g:100 b:200
saturation 0.50 = r:100 g:134 b:200
saturation 0.25 = r:150 g:167 b:200
I've noticed with saturation changing, the max value (blue channel) doesn't change.
There is no found ratio between the R-G-B numbers.
Can I calculate a new shade of color using RGB and not convert it first to HSV color?
After Excel plotting, and hours of complex formula constructing, the solution wasn't really that complex!

It contains a bit of JQuery to find the index of the max and min rgb values.
function saturation(i,v) { //i as in input, v as saturation value
var min = $.inArray(Math.min.apply(this, i), i), //returns the index of min, max and mid.
max = $.inArray(Math.max.apply(this, i), i),
mid = parseInt([0, 1, 2].filter(function (j) {return Array(min, max).indexOf(j) < 0;})),
r = (i[max] - i[mid]) / (i[mid] - i[min]), //ratio, because it is always constant,
//we use this to calc mid value
o = []; //o as in output
if (min !== max) {
o[max] = Math.round(i[max]);
o[min] = Math.round(i[max] * (1 - v));
o[mid] = Math.round(o[max] / (r + 1) * v + i[max] * (1 - v));
}
return o;
}
saturation([52,132,220], 0.5) //Array [ 110, 162, 220 ]
saturation([52,132,220], 0) //Array [ 220, 220, 220 ]
saturation([52,132,220], 1) //Array [ 0, 105, 220 ]
Wow! I'm proud of myself! It's really late here.
I recommend you plot a graph if you are ever having trouble dealing with formulae regarding color.
Using knowledge of OP's answer that the middle value has
new_mid = max / (r + 1) * sat + max * (1 - sat)
r = (max - mid) / (mid - min)
Can simplify...
new_mid = max / (((max - mid) / (mid - min)) + 1) * sat + max * (1 - sat)
= max * (sat / (((max - mid) / (mid - min)) + 1) + (1 - sat))
= max * ((1 - sat) + sat / (((max - mid) / (mid - min)) + ((mid - min) / (mid - min))))
= max * ((1 - sat) + sat / (((max - mid) + (mid - min)) / (mid - min)))
= max * ((1 - sat) + sat * (mid - min) / ((max - mid) + (mid - min)))
= max * ((1 - sat) + sat * (mid - min) / (max - min))
let
a = max - min
b = mid - min
=>
new_mid = max * ((1 - sat) + sat * b / a)
I've re-written the method to not use jQuery, do division less and accept more inputs
// Inputs
// Array rgb colour data, s new hsl saturation
// Outputs
// Array rgb colour data
function saturation(rgb, s) {
var min = rgb.indexOf(Math.min.apply(null, rgb)), // index of min
max = rgb.indexOf(Math.max.apply(null, rgb)), // index of max
mid = [0, 1, 2].filter(function (i) {return i !== min && i !== max;})[0],
a = rgb[max] - rgb[min],
b = rgb[mid] - rgb[min],
x = rgb[max],
arr = [x, x, x];
if (min === max) {
min = 2; // both max = min = 0, => mid = 1, so set min = 2
a = 1; // also means a = b = 0, don't want division by 0 in `b / a`
}
arr[max] = x;
arr[min] = Math.round(x * (1 - s));
arr[mid] = Math.round(x * ((1 - s) + s * b / a));
return arr;
}
Now same as before,
saturation([52, 132, 220], 0.5); // [ 110, 162, 220 ]
saturation([52, 132, 220], 0); // [ 220, 220, 220 ]
saturation([52, 132, 220], 1); // [ 0, 105, 220 ]
But also works for values which have equal sections
saturation([80, 80, 80], 0.5); // [80, 40, 40] vs []
Changing value is a lot easier
// Inputs
// Array rgb colour data, v new hsl value
// Outputs
// Array rgb colour data
function nvalue(rgb, v) {
var x = Math.max.apply(null, rgb);
if (x === 0)
return [
Math.round(255 * v),
Math.round(255 * v),
Math.round(255 * v)
];
x = 255 * v / x;
return [
Math.round(rgb[0] * x),
Math.round(rgb[1] * x),
Math.round(rgb[2] * x)
];
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With