Converting HSV, HSB and RGB
HSV is the most frequently implemented color conversion: it sits behind every color picker. This page collects three versions of the algorithm and a direct path to HSL. The model itself is described on the HSV and HSB models page.
RGB to HSV
HSV to RGB: three versions
1. Sectors. The classic version with a table of six cases:
2. One formula per component. No branches, handy in shaders and spreadsheets:
3. Via the pure color. First the color of the angle (see hue angle to RGB), then one multiplication:
All three give the same result. For HSV (15°, 80%, 78.4%):
An integer version
On microcontrollers and in old graphics code HSV is computed without floating point: H in 0..359, S and V in 0..255. The sector is H / 60, and the position within the sector is the remainder scaled to 0..255. Every division by 255 is rounded:
const div255 = (x) => ((x + 127) / 255) | 0; // x / 255, rounded
// h: 0..359, s, v: 0..255, result 0..255
function hsvToRgb8(h, s, v) {
const sector = (h / 60) | 0;
const f = (((h % 60) * 255 + 30) / 60) | 0; // position in sector 0..255
const p = div255(v * (255 - s));
const q = div255(v * (255 - div255(s * f)));
const t = div255(v * (255 - div255(s * (255 - f))));
switch (sector) {
case 0: return [v, t, p];
case 1: return [q, v, p];
case 2: return [p, v, t];
case 3: return [p, q, v];
case 4: return [t, p, v];
default: return [v, p, q];
}
}
For HSV (15, 204, 200) the result is exactly (200, 80, 40). Across the whole range it differs from the floating-point version by at most 1. A faster variant using >> 8, i.e. dividing by 256 instead of 255, is common. It can be off by 2, and a gray (S = 0, V = 128) comes out as (127, 127, 128). For driving LEDs that does not matter; for graphics, divide by 255.
HSV to HSL and back
Hue stays the same. The other two components:
For our color: HSV (15°, 80%, 78.4%) ↔ HSL (15°, S 66.7%, L 47.1%).
Code (floating-point version)
function rgbToHsv(r, g, b) {
const max = Math.max(r, g, b), min = Math.min(r, g, b), d = max - min;
let h = 0;
if (d > 1e-9) {
h = max === r ? (g - b) / d : max === g ? (b - r) / d + 2 : (r - g) / d + 4;
h = (h * 60 + 360) % 360;
}
return { h, s: max === 0 ? 0 : d / max, v: max };
}
function hsvToRgb(h, s, v) {
const f = (n) => {
const k = (n + h / 60) % 6;
return v - v * s * Math.max(0, Math.min(k, 4 - k, 1));
};
return { r: f(5), g: f(3), b: f(1) };
}