Converting HSI and RGB
HSI computes hue geometrically, so the way back to RGB also looks different from HSV and HSL: instead of six 60° sectors there are three 120° ones, with a cosine in the middle. The model itself is described on the HLS and HSI models page.
RGB to HSI
The atan2 formula for H gives the same result as the classic arccos form (see RGB to hue angle) and is simpler in code.
HSI to RGB
The wheel splits into three sectors. In each, one component is the smallest and equals I · (1 - S):
Example both ways
RGB (200, 80, 40), #c85028:
Results out of range
In HSV and HSL every triple of in-range values gives valid RGB. In HSI it does not: HSI with I and S over the full 0..1 range is larger than the RGB cube. Example:
R = 1.8 does not exist in RGB. Pure red has an HSI intensity of only 1/3, so full saturation cannot be combined with higher intensity. The result has to be clipped to 0..1 (which changes the color) or S reduced. When editing an image in HSI, check this after every change.
Code
const DEG = Math.PI / 180;
function rgbToHsi(r, g, b) {
const i = (r + g + b) / 3;
const s = i === 0 ? 0 : 1 - Math.min(r, g, b) / i;
let h = Math.atan2(Math.sqrt(3) * (g - b), 2 * r - g - b) / DEG;
if (h < 0) h += 360;
return { h: s === 0 ? 0 : h, s, i };
}
function hsiToRgb(h, s, i) {
h = ((h % 360) + 360) % 360;
const sector = Math.floor(h / 120); // 0: RG, 1: GB, 2: BR
const hh = (h - 120 * sector) * DEG;
const low = i * (1 - s);
const high = i * (1 + (s * Math.cos(hh)) / Math.cos(60 * DEG - hh));
const mid = 3 * i - (low + high);
if (sector === 0) return { r: high, g: mid, b: low };
if (sector === 1) return { r: low, g: high, b: mid };
return { r: mid, g: low, b: high };
}