JS: Converting RGB colorspace to HSV

When writing a program which deals with color, there might be a need to convert a color representation from a colorspace to another. Here we will learn how to convert a color from RGB to HSV.

2011 is the year I started writing a color picker plugin for jQuery. Due to the nature of a color wheel, it is easier to work in HSV. However, colors in CSS are represented by RGB values. Hence, a conversion between RGB to HSV (and vice versa) is important.

Basically the algorithm to convert RGB to HSV is all about math. That’s why even though I write the codes in JavaScript, you are actually able to implement it in any languages without any additional libraries necessary.

The following is a snippet for RGB to HSV conversion function I wrote in jQuery Wheel Color Picker. The function accepts R, G, and B parameters ranging from 0 – 255.

function rgbToHsv( r, g, b ) {
    	
	var h;
	var s;
	var v;
     
	var maxColor = Math.max(r, g, b);
	var minColor = Math.min(r, g, b);
	var delta = maxColor - minColor;
    	
	// Calculate hue
	// To simplify the formula, we use 0-6 range.
	if(delta == 0) {
		h = 0;
	}
	else if(r == maxColor) {
		h = (6 + (g - b) / delta) % 6;
	}
	else if(g == maxColor) {
		h = 2 + (b - r) / delta;
	}
	else if(b == maxColor) {
		h = 4 + (r - g) / delta;
	}
	else {
		h = 0;
	}
	// Then adjust the range to be 0-1
	h = h/6;
	
	// Calculate saturation
	if(maxColor != 0) {
		s = delta / maxColor;
	}
	else {
		s = 0;
	}
	
	// Calculate value
	v = maxColor / 255;
	
	return { h: h, s: s, v: v };
};

Explanation

HSL vs HSV color models

A HSV and HSB color space has the following components:

  • Hue
  • Saturation
  • Value or Brightness

Here our objective is to calculate the value of each component H, S, and V. On the above snippet, the function to do the conversion is called rgbToHsv(). First, let’s define the function, accepting three parameters, R, G, and B. Also, we prepare three variables to store H, S, and V values.

R, G, and B values are represented in 0 – 255 scale, while H, S, and V values are represented in decimal 0 – 1 scale.

The rgbToHsv() function shall return all three values as an object.

function rgbToHsv( r, g, b ) {
  var h;
  var s;
  var v;
  // HSV calculations go here...
  return { h: h, s: s, v: v };
}

Calculating Hue

	var maxColor = Math.max(r, g, b);
	var minColor = Math.min(r, g, b);
	var delta = maxColor - minColor;
 
	// Calculate hue
	// To simplify the formula, we use 0-6 range.
	if(delta == 0) {
		h = 0;
	}
	else if(r == maxColor) {
		h = (6 + (g - b) / delta) % 6;
	}
	else if(g == maxColor) {
		h = 2 + (b - r) / delta;
	}
	else if(b == maxColor) {
		h = 4 + (r - g) / delta;
	}
	else {
		h = 0;
	}
	// Then adjust the range to be 0-1
	h = h/6;

The Hue component can be represented as a gradient of red to green, to blue, then back to red. When we split the gradient into each R, G, B components, we get six segments.

Relationship between RGB and Hue
SegmentRGB
11 (max)0..10 (min)
20..11 (max)0 (min)
30 (min)1 (max)0..1
40 (min)0..11 (max)
50..10 (min)1 (max)
61 (max)0 (min)0..1

To implement the conversion, the value of Y axis on the graph may be determined by finding the maximum or minimum values between the three components of RGB. To make it simpler, let’s change the scale of X axis to 0..6.

Let’s take this color for an example, ██████ (#3465a4) which has the following RGB values:
R: 52, G: 101, B: 164

We know that the minimum value is 52 (R), and the maximum value is 164 (B). The difference (delta) between max and min is 112.

An exception to this is when all three components have the same value (max = min), the Hue cannot be determined because it falls in a greyscale. For this exception, any value for Hue doesn’t matter, so let’s just assign H to 0 to avoid error.

Now there are a few conditions to determine which segment (X axis) the Hue belongs to:

If R is max and G is min, then H belongs to segment 6 (5 ≤ H < 6).
If R is max and B is min, then H belongs to segment 1 (0 ≤ H < 1).

If G is max and B is min, then H belongs to segment 2 (1 ≤ H < 2).
If G is max and R is min, then H belongs to segment 3 (2 ≤ H < 3).

If B is max and R is min, then H belongs to segment 4 (3 ≤ H < 4).
If B is max and G is min, then H belongs to segment 5 (4 ≤ H < 5).

For our example, B is max and R is min, hence H value is between 3.0 to 4.0 (segment 4). To find the exact value, we compare the intensity of the other component G relative to delta. See again the above graph, then we shall know that the higher G is, the position of H on the X axis goes further to the left. Conversely, the lower G is, the further H goes to the right. Therefore H can be calculated with the following formula.

H = 4 - (G / delta)
= 4 - (101 / 112)
= 3.098214286

If B is max and G is min, the value of H will depend on the other component R but the formula is reversed. The higher R is, the further H goes to the right, and the lower R is, the further H goes to the left. Hence the formula is:

H = 3 + (R / delta)

In case of B is max, both the above formulas can be combined to become:

H = 2 + (R - G) / delta

The formulas for the case of R max or G max are similar.

The result of the above calculation still uses 0..6 scale. Our expected value is between 0..1, so we divide the result by 6.

If the gradient of Hue is represented as a full circle, the value of H in degree unit may be determined by multiplying the result by 360.

Calculating Saturation

	// Calculate saturation
	if(maxColor != 0) {
		s = delta / maxColor;
	}
	else {
		s = 0;
	}
     

Saturation tells how much the intensity of the color is. The higher S is, the more colorful it is. If S equals to 0, the color is a shade of grey. In RGB colorspace, the value of S may be determined by finding the difference between the highest and lowest (delta) of the three components. Please note that the difference is relative to the highest value (maxColor). To get the absolute value in 0..1 scale, divide the result by maxColor.

S = ( max(R,G,B) - min(R,G,B) ) / max(R,G,B)

Back to our example color, ██████ (#3465a4) which has the following RGB components:
R: 52, G: 101, B: 164

S can be calculated using the above formula.

S = ( max(R,G,B) - min(R,G,B) ) / max(R,G,B)
= ( 164 - 52 ) / 164
= 0.68292682926829

Again an exception here, when the color is absolute black (#000000), maxColor is 0 and we will encounter an error (division by zero). So a conditional if to check the value of maxColor is needed here as well.

Calculating Value / Brightness

	// Calculate value
	v = maxColor / 255;

Calculating Brightness or Value is very easy. Just get the highest value of the three R, G, B components, that’s it.

Still, the value is in 0..255 range and we want 0..1 range. Therefore divide maxColor by 255.

Our example again, ██████ (#3465a4) has the following RGB components (you probably remembered the values already by now):
R: 52, G: 101, B: 164

Guess what the value of V is? You are right, 164.

V = max(R,G,B) / 255
= 164 / 255
= 0.643137255

Examples

After finishing the function rgbToHsv(), let’s put it on action.

var r = prompt("Nilai R");
var g = prompt("Nilai G");
var b = prompt("Nilai B");
var hsv = rgbToHsv(r, g, b);
alert("Color rgb(" + r + "," + g + "," + b + ") in HSV is \nH: " + hsv.h + ", S: " + hsv.s + ", V: " + hsv.v);

That’s how to convert RGB to HSV colorspace! Now I challenge you to make a reverse conversion from HSV to RGB 🙂