Type a color in any notation, #3b82f6, rgb(59
130 246) or hsl(217 91% 60%), and the other
two fields fill in. Use it when the design file gives you HEX and
your CSS custom properties store HSL, so you can adjust lightness
in one place.
Pick the notation for what you need to edit. All three describe the same sRGB space. HEX is compact and copies cleanly out of a design file. RGB exposes the three channels. HSL separates hue from saturation and lightness, which is why theme systems often store colors that way: a hover state becomes one number changed instead of three recalculated.
For example, hsl(217 91% 60%) with lightness dropped
to 50% gives a darker blue of the same hue, and you never touch
the hue or saturation.
HEX to RGB takes a calculator and a minute. HSL does not work that way: hue is an angle, saturation and lightness are percentages of a computed range, and the rounding is not obvious. A hand conversion is usually a shade or two off, which is enough to make a border look wrong next to the same color pasted from elsewhere.
Keep HEX or RGB as the source of truth. This tool rounds HSL to whole numbers, so a HEX to HSL to HEX round trip can land one unit off in a channel. HEX and RGB convert exactly. The converter also reads only opaque colors: 3- or 6-digit HEX, and the first three numbers of an rgb() or hsl() value, so alpha is dropped.
The preview swatch updates as you type, and each field has its own copy button. The math runs in the page, so nothing is uploaded.
function hexToRgb(hex) {
let h = hex.replace("#", "");
if (h.length === 3) h = [...h].map((c) => c + c).join(""); // #abc -> #aabbcc
if (!/^[0-9a-f]{6}$/i.test(h)) throw new Error("Invalid HEX: " + hex);
const n = parseInt(h, 16);
return { r: n >> 16, g: (n >> 8) & 255, b: n & 255 };
}
function rgbToHex({ r, g, b }) {
return "#" + [r, g, b].map((v) => v.toString(16).padStart(2, "0")).join("");
}
function rgbToHsl({ r, g, b }) {
r /= 255; g /= 255; b /= 255;
const max = Math.max(r, g, b), min = Math.min(r, g, b), l = (max + min) / 2;
if (max === min) return { h: 0, s: 0, l: Math.round(l * 100) };
const d = max - min;
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
let h = max === r ? (g - b) / d + (g < b ? 6 : 0) : max === g ? (b - r) / d + 2 : (r - g) / d + 4;
return { h: Math.round(h * 60), s: Math.round(s * 100), l: Math.round(l * 100) };
}
const rgb = hexToRgb("#3b82f6");
console.log(rgb); // { r: 59, g: 130, b: 246 }
console.log(rgbToHex(rgb)); // #3b82f6
console.log(rgbToHsl(rgb)); // { h: 217, s: 91, l: 60 } In the browser, the engine can do the conversion for you: set el.style.color = "#3b82f6" and read getComputedStyle(el).color to get rgb().
<?php
function hexToRgb(string $hex): array {
$hex = ltrim($hex, "#");
if (strlen($hex) === 3) {
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
}
if (!preg_match('/^[0-9a-f]{6}$/i', $hex)) {
throw new InvalidArgumentException("Invalid HEX: $hex");
}
return array_map("hexdec", str_split($hex, 2)); // [r, g, b]
}
function rgbToHex(int $r, int $g, int $b): string {
return sprintf("#%02x%02x%02x", $r, $g, $b);
}
function rgbToHsl(int $r, int $g, int $b): array {
$r /= 255; $g /= 255; $b /= 255;
$max = max($r, $g, $b); $min = min($r, $g, $b);
$l = ($max + $min) / 2;
if ($max == $min) return [0, 0, round($l * 100)];
$d = $max - $min;
$s = $l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min);
$h = match ($max) {
$r => ($g - $b) / $d + ($g < $b ? 6 : 0),
$g => ($b - $r) / $d + 2,
default => ($r - $g) / $d + 4,
};
return [round($h * 60), round($s * 100), round($l * 100)];
}
[$r, $g, $b] = hexToRgb("#3b82f6");
echo "rgb($r, $g, $b)", PHP_EOL; // rgb(59, 130, 246)
echo rgbToHex($r, $g, $b), PHP_EOL; // #3b82f6
[$h, $s, $l] = rgbToHsl($r, $g, $b);
echo "hsl($h, $s%, $l%)", PHP_EOL; // hsl(217, 91%, 60%) match() requires PHP 8.0; on older versions replace it with an if/elseif chain.
import colorsys
def hex_to_rgb(value: str) -> tuple[int, int, int]:
h = value.lstrip("#")
if len(h) == 3:
h = "".join(c * 2 for c in h)
if len(h) != 6:
raise ValueError(f"Invalid HEX: {value}")
return tuple(int(h[i:i + 2], 16) for i in (0, 2, 4))
def rgb_to_hex(r: int, g: int, b: int) -> str:
return f"#{r:02x}{g:02x}{b:02x}"
def rgb_to_hsl(r: int, g: int, b: int) -> tuple[int, int, int]:
# colorsys works in 0..1 and returns H, L, S in that order
h, l, s = colorsys.rgb_to_hls(r / 255, g / 255, b / 255)
return round(h * 360), round(s * 100), round(l * 100)
rgb = hex_to_rgb("#3b82f6")
print(rgb) # (59, 130, 246)
print(rgb_to_hex(*rgb)) # #3b82f6
print(rgb_to_hsl(*rgb)) # (217, 91, 60) colorsys returns HLS order (hue, lightness, saturation), so swap the last two values before writing hsl().
HEX and RGB are the same value written two ways. Both encode a 24-bit sRGB color, so converting between them loses nothing. HSL is a different coordinate system over the same colors: hue is an angle around the wheel, saturation is distance from gray, and lightness runs from black to white. It stores no extra information. Designers use it because "a bit lighter, a bit less saturated" is easier to reason about than three channel values.
Store theme colors as HSL parts to get variants by arithmetic. A hover state is the same hue and saturation with lightness dropped by 10. A muted version keeps hue and lightness and lowers saturation. For example, store the three numbers as separate custom properties, and hsl(var(--h) var(--s) var(--l) / 0.5) gives you a translucent version without converting anything.
This tool follows the specifications below. Read them when you need the exact normative wording, for example to settle an edge case between two implementations.
HEX (3- or 6-digit), RGB and HSL. Type a value in any one field and the other two update as soon as the value is complete.
Use HSL when you need to adjust a color by hand. Lower lightness by 10 for a hover state, or lower saturation for a muted variant, and the hue stays put. Use HEX when you are copying values between a design file and CSS, since it is compact and every tool accepts it.
No. The converter reads opaque colors only, and 8-digit HEX such as #3b82f6cc is not accepted. To add transparency, take the rgb() or hsl() output and add an alpha value, for example rgb(59 130 246 / 0.5).
HEX and RGB convert exactly. HSL is rounded to whole numbers, so converting HSL back to HEX can land one unit off in a channel. Keep HEX or RGB as your source of truth.
Yes. The swatch next to the fields shows the current color and updates as you type, so you can check it before copying.
Once you have the values, the CSS gradient generator blends between them. The CSS formatter tidies the stylesheet afterwards.