HEX, RGB and HSL are three notations for describing colour on the web: HEX and RGB both specify red, green and blue channel amounts (HEX in base 16, RGB in decimal), while HSL re-describes the very same colours as a hue angle, a saturation percentage, and a lightness percentage. None is more powerful than the others for standard sRGB colours — they map to identical pixels — but each is easier to reason about for different jobs. Understanding how they relate lets you pick the right one and convert confidently between them.
How does RGB describe a colour?
RGB gives the intensity of three light channels — red, green and blue — each from 0 to 255, and the screen adds them together to make the final colour. This is additive colour: all three at zero is black (no light), all three at maximum is white (full light of every channel), and equal amounts of all three produce a grey. Pure red is rgb(255, 0, 0); mixing full red and full green yields yellow, rgb(255, 255, 0), which surprises people expecting paint-like mixing but is exactly how emitted light behaves.
rgb(255, 0, 0) /* red */
rgb(0, 128, 0) /* a mid green */
rgb(30, 144, 255) /* dodger blue */
rgb(30 144 255 / 0.5) /* the same blue at 50% opacity */
Modern CSS also accepts channels as percentages and a slash-separated alpha, but the core idea is unchanged: three numbers, one per primary, that say how much of each light the pixel emits.
Why is HEX the same thing as RGB?
A HEX colour is just the RGB channel values written in hexadecimal and concatenated, so every HEX code has an exact RGB twin. Hexadecimal is base 16, using digits 0–9 then A–F, and two hex digits can represent 0 to 255 — precisely the range of one RGB channel. So a six-digit HEX code is three channel pairs in red-green-blue order:
| HEX | Red | Green | Blue | RGB equivalent |
|---|---|---|---|---|
| #FF0000 | FF = 255 | 00 = 0 | 00 = 0 | rgb(255, 0, 0) |
| #1E90FF | 1E = 30 | 90 = 144 | FF = 255 | rgb(30, 144, 255) |
| #808080 | 80 = 128 | 80 = 128 | 80 = 128 | rgb(128, 128, 128) |
To convert a hex pair to decimal by hand, multiply the first digit by 16 and add the second: 90 is 9 x 16 + 0 = 144, and 1E is 1 x 16 + 14 = 30. The three-digit shorthand like #1E9 does not exist; the real shorthand is #f80, which expands by doubling each digit to #ff8800. A fourth pair adds alpha, so #1E90FF80 is that blue at roughly 50% opacity because 80 is 128 of 255.
What do the HSL values represent?
HSL re-expresses a colour as hue (an angle on the colour wheel), saturation (how vivid it is), and lightness (how close to black or white), which maps far better onto how people think about adjusting colour. Hue runs 0 to 360 degrees: red at 0, yellow near 60, green near 120, cyan near 180, blue near 240, magenta near 300, and back to red. Saturation goes from 0% (a pure grey) to 100% (fully vivid). Lightness runs from 0% (black) through 50% (the pure hue) up to 100% (white).
hsl(0, 100%, 50%) /* red */
hsl(210, 100%, 56%) /* dodger blue */
hsl(210, 100%, 75%) /* a lighter tint of that blue */
hsl(210, 40%, 56%) /* a muted, less saturated version */
The reason HSL is pleasant to edit by hand is that each knob does one recognisable thing. Want a lighter version of a colour? Raise lightness. Want it more muted? Lower saturation. Want a related colour? Rotate the hue. Doing the equivalent in RGB or HEX means changing all three channels in ways that are hard to predict. The trade-off, worth knowing, is that HSL lightness is a mathematical midpoint rather than a perceptual one, so two hues at the same lightness can still look unequal in brightness.
How do I convert between the three formats?
HEX and RGB convert by base conversion alone, while RGB and HSL convert through a short geometric formula that finds the maximum and minimum channel. The HEX–RGB direction is mechanical: split into pairs, convert each pair between base 16 and base 10. The RGB–HSL direction takes the largest and smallest of the three channels; lightness is their average, saturation depends on their spread, and hue depends on which channel is the maximum and by how much the others trail it.
You rarely need to do the HSL trigonometry by hand, and getting it exactly right is fiddly, so this is a natural place to lean on a client-side colour converter that computes all three notations from any one input locally in your browser. That keeps the values off any server — which matters if the colour is part of unreleased branding — while removing the arithmetic risk. The key mental model to carry is that the conversion never changes the colour; it only re-labels the same pixel three different ways.
| Same colour | Notation |
|---|---|
| Dodger blue | #1E90FF |
| Dodger blue | rgb(30, 144, 255) |
| Dodger blue | hsl(210, 100%, 56%) |
How does transparency work across formats?
All three formats can carry an alpha channel that controls opacity, where 0 is fully transparent and the maximum is fully opaque. In functional notation you add a fourth value: rgb(30 144 255 / 0.5) and hsl(210 100% 56% / 0.5) are both the blue at half opacity, with alpha as a 0-to-1 number or a percentage. In HEX you append a fourth pair, so alpha uses the same 00–FF range as the colour channels: #1E90FF80 is about 50% because 80 is 128 out of 255, and #1E90FFFF is fully opaque.
One caution: alpha blends the element against whatever is behind it, so a semi-transparent colour is not a fixed colour — it depends on its backdrop. When you need a specific final appearance regardless of background, use a fully opaque colour rather than a transparent one over an assumed backdrop.
Which colour format should I choose?
Pick the notation that makes the task clearest, since for sRGB colours they are interchangeable. HEX is compact and universally recognised, which is why it dominates design handoffs and brand guidelines; reach for it when you have a fixed value and just need to drop it in. RGB is the natural choice when you are thinking in terms of channels or compositing, or when working with code that manipulates individual colour components. HSL wins whenever you are adjusting colour by hand — building hover states, deriving tints and shades, or generating a set of related hues — because each parameter maps to an intuitive action.
It is also worth knowing where these three formats reach their limits, because that explains why newer notations exist. HEX, RGB and HSL all describe colours within the sRGB gamut — the colour range standard displays have used for years. Modern screens can show a wider range, and functions like oklch() and color() were added so CSS can address those extra colours and describe lightness in a way that matches perception. None of that makes HEX, RGB or HSL obsolete; the overwhelming majority of interface colour still lives comfortably in sRGB, and these three notations remain the clearest way to express it. But if you ever find that a colour on a capable display looks more vivid than any HEX code can capture, that is the boundary of sRGB making itself felt, not a mistake in your conversion.
Many codebases mix all three deliberately: HEX for imported brand colours, HSL for computed variations, and RGB where alpha compositing is the concern. There is no single correct answer, only the right tool for the moment. And when you need the same colour in a different notation, remember that conversion is lossless for standard colours — the pixel stays put; only the description changes.