How to Convert Colors (HEX to RGB to HSL)
Working with colors in web development means converting between different formats. HEX is great for CSS, RGB is useful for manipulation, and HSL is intuitive for adjustments. This tutorial shows you how to convert between all major color formats in JavaScript.
Color Formats Explained
| Format | Example | Use Case |
|--------|---------|----------|
| HEX | #3B82F6 | CSS, design tools |
| RGB | rgb(59, 130, 246) | Programmatic manipulation |
| RGBA | rgba(59, 130, 246, 0.5) | With transparency |
| HSL | hsl(217, 91%, 60%) | Intuitive adjustments |
| HSLA | hsla(217, 91%, 60%, 0.5) | HSL with transparency |
HEX to RGB Conversion
function hexToRgb(hex) {
// Remove the # if present
hex = hex.replace(/^#/, ''); // Parse hex values
const r = parseInt(hex.slice(0, 2), 16);
const g = parseInt(hex.slice(2, 4), 16);
const b = parseInt(hex.slice(4, 6), 16);
return { r, g, b };
}
// Usage
const rgb = hexToRgb('#3B82F6');
console.log(rgb);
// { r: 59, g: 130, b: 246 }
// Or as CSS string
function hexToRgbString(hex) {
const { r, g, b } = hexToRgb(hex);
return rgb(${r}, ${g}, ${b});
}
console.log(hexToRgbString('#3B82F6'));
// "rgb(59, 130, 246)"
Handling Short HEX (3-digit)
function hexToRgb(hex) {
hex = hex.replace(/^#/, ''); // Expand 3-digit hex to 6-digit
if (hex.length === 3) {
hex = hex.split('').map(c => c + c).join('');
}
const r = parseInt(hex.slice(0, 2), 16);
const g = parseInt(hex.slice(2, 4), 16);
const b = parseInt(hex.slice(4, 6), 16);
return { r, g, b };
}
// Usage
hexToRgb('#3BF'); // Same as #33BBFF
// { r: 51, g: 187, b: 255 }
RGB to HEX Conversion
function componentToHex(c) {
// Clamp value between 0 and 255
const clamped = Math.max(0, Math.min(255, c));
const hex = clamped.toString(16);
return hex.length === 1 ? '0' + hex : hex;
}function rgbToHex(r, g, b) {
return '#' + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
// Usage
const hex = rgbToHex(59, 130, 246);
console.log(hex);
// "#3b82f6"
RGB to HSL Conversion
function rgbToHsl(r, g, b) {
// Normalize to 0-1 range
r /= 255;
g /= 255;
b /= 255; const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
let h, s;
const l = (max + min) / 2;
if (max === min) {
// Achromatic (gray)
h = s = 0;
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
break;
case g:
h = ((b - r) / d + 2) / 6;
break;
case b:
h = ((r - g) / d + 4) / 6;
break;
}
}
// Convert to degrees and percentages
return {
h: Math.round(h 360),
s: Math.round(s 100),
l: Math.round(l 100)
};
}
// Usage
const hsl = rgbToHsl(59, 130, 246);
console.log(hsl);
// { h: 217, s: 91, l: 60 }
// As CSS string
function rgbToHslString(r, g, b) {
const { h, s, l } = rgbToHsl(r, g, b);
return hsl(${h}, ${s}%, ${l}%);
}
console.log(rgbToHslString(59, 130, 246));
// "hsl(217, 91%, 60%)"
HSL to RGB Conversion
function hueToRgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) 6 t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) (2/3 - t) 6;
return p;
}function hslToRgb(h, s, l) {
// Normalize to 0-1 range
h /= 360;
s /= 100;
l /= 100;
let r, g, b;
if (s === 0) {
// Achromatic
r = g = b = l;
} else {
const q = l < 0.5 ? l
(1 + s) : l + s - l s;
const p = 2 l - q; r = hueToRgb(p, q, h + 1/3);
g = hueToRgb(p, q, h);
b = hueToRgb(p, q, h - 1/3);
}
return {
r: Math.round(r 255),
g: Math.round(g 255),
b: Math.round(b 255)
};
}
// Usage
const rgb = hslToRgb(217, 91, 60);
console.log(rgb);
// { r: 59, g: 130, b: 246 }
HEX to HSL and HSL to HEX
function hexToHsl(hex) {
const { r, g, b } = hexToRgb(hex);
return rgbToHsl(r, g, b);
}function hslToHex(h, s, l) {
const { r, g, b } = hslToRgb(h, s, l);
return rgbToHex(r, g, b);
}
// Usage
hexToHsl('#3B82F6');
// { h: 217, s: 91, l: 60 }
hslToHex(217, 91, 60);
// "#3b82f6"
Complete Color Converter Class
class ColorConverter {
// Parse any color format to RGB
static parse(color) {
if (typeof color !== 'string') return null;
color = color.trim().toLowerCase(); // HEX: #3B82F6 or #3BF
if (color.startsWith('#')) {
return this.hexToRgb(color);
}
// RGB/RGBA: rgb(59, 130, 246) or rgba(59, 130, 246, 0.5)
const rgbMatch = color.match(/rgba?\((\d+),\s(\d+),\s(\d+)(?:,\s([\d.]+))?\)/);
if (rgbMatch) {
return {
r: parseInt(rgbMatch[1]),
g: parseInt(rgbMatch[2]),
b: parseInt(rgbMatch[3]),
a: rgbMatch[4] !== undefined ? parseFloat(rgbMatch[4]) : 1
};
}
// HSL/HSLA: hsl(217, 91%, 60%) or hsla(217, 91%, 60%, 0.5)
const hslMatch = color.match(/hsla?\((\d+),\s(\d+)%,\s(\d+)(?:%,\s([\d.]+))?\)/);
if (hslMatch) {
const h = parseInt(hslMatch[1]);
const s = parseInt(hslMatch[2]);
const l = parseInt(hslMatch[3]);
const a = hslMatch[4] !== undefined ? parseFloat(hslMatch[4]) : 1;
return { ...this.hslToRgb(h, s, l), a };
}
return null;
}
static hexToRgb(hex) {
hex = hex.replace(/^#/, '');
if (hex.length === 3) {
hex = hex.split('').map(c => c + c).join('');
}
return {
r: parseInt(hex.slice(0, 2), 16),
g: parseInt(hex.slice(2, 4), 16),
b: parseInt(hex.slice(4, 6), 16),
a: 1
};
}
static rgbToHex(r, g, b) {
const toHex = c => {
const clamped = Math.max(0, Math.min(255, c));
const hex = clamped.toString(16);
return hex.length === 1 ? '0' + hex : hex;
};
return '#' + toHex(r) + toHex(g) + toHex(b);
}
static rgbToHsl(r, g, b) {
r /= 255;
g /= 255;
b /= 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
let h, s;
const l = (max + min) / 2;
if (max === min) {
h = s = 0;
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break;
case g: h = ((b - r) / d + 2) / 6; break;
case b: h = ((r - g) / d + 4) / 6; break;
}
}
return {
h: Math.round(h 360),
s: Math.round(s 100),
l: Math.round(l 100)
};
}
static hslToRgb(h, s, l) {
h /= 360;
s /= 100;
l /= 100;
let r, g, b;
if (s === 0) {
r = g = b = l;
} else {
const hueToRgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) 6 t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) (2/3 - t) 6;
return p;
};
const q = l < 0.5 ? l (1 + s) : l + s - l s;
const p = 2 l - q;
r = hueToRgb(p, q, h + 1/3);
g = hueToRgb(p, q, h);
b = hueToRgb(p, q, h - 1/3);
}
return {
r: Math.round(r 255),
g: Math.round(g 255),
b: Math.round(b 255)
};
}
// Convert any color to any format
static convert(color, format) {
const rgb = this.parse(color);
if (!rgb) return null;
switch (format) {
case 'hex':
return this.rgbToHex(rgb.r, rgb.g, rgb.b);
case 'rgb':
return rgb(${rgb.r}, ${rgb.g}, ${rgb.b});
case 'rgba':
return rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${rgb.a});
case 'hsl': {
const hsl = this.rgbToHsl(rgb.r, rgb.g, rgb.b);
return hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%);
}
case 'hsla': {
const hsl = this.rgbToHsl(rgb.r, rgb.g, rgb.b);
return hsla(${hsl.h}, ${hsl.s}%, ${hsl.l}%, ${rgb.a});
}
case 'object':
return rgb;
default:
return null;
}
}
// Generate color variations
static lighten(color, percent) {
const rgb = this.parse(color);
const hsl = this.rgbToHsl(rgb.r, rgb.g, rgb.b);
hsl.l = Math.min(100, hsl.l + percent);
return this.hslToHex(hsl.h, hsl.s, hsl.l);
}
static darken(color, percent) {
return this.lighten(color, -percent);
}
static saturate(color, percent) {
const rgb = this.parse(color);
const hsl = this.rgbToHsl(rgb.r, rgb.g, rgb.b);
hsl.s = Math.min(100, hsl.s + percent);
return this.hslToHex(hsl.h, hsl.s, hsl.l);
}
}
// Usage
ColorConverter.convert('#3B82F6', 'hsl');
// "hsl(217, 91%, 60%)"
ColorConverter.convert('hsl(217, 91%, 60%)', 'hex');
// "#3b82f6"
ColorConverter.lighten('#3B82F6', 10);
// Lighter blue
ColorConverter.darken('#3B82F6', 10);
// Darker blue
Try It Online
Need quick color conversions? Build a color converter tool or check out related developer tools at DevKits.
Conclusion
Converting between color formats is essential for dynamic theming, color pickers, and design tools. The ColorConverter class above handles all major formats and includes utilities for lightening, darkening, and saturating colors.
For more JavaScript utilities, see How to Minify CSS.
---
Related Tools:
- Color Converter (coming soon)
- CSS Minifier
- JSON Formatter