Color Models Explained
Every color you see on a screen is, underneath, just three numbers — how much red, green and blue light each pixel emits. #3366cc, rgb(51, 102, 204) and hsl(220, 60%, 50%) look like three different things, but they are three spellings of that same blue. This guide builds color from first principles: why RGB is about adding light, how HEX is nothing more than RGB written in base-16, why HSL is the friendlier notation for adjusting a color by hand, what the alpha channel actually does, and — worked out by hand — how one color moves between all three. It ends with the honest catch: they all live in the sRGB color space, and modern wide-gamut screens can do more.
RGB: colors are made by adding light
A screen has no paint. Each pixel is three tiny light sources — one red, one green, one blue — and a color is just how brightly each of the three is driven. This is additive color: you start from black (all off) and add light to get brighter. Red plus green light makes yellow; all three at full makes white. That is the opposite of mixing paint or ink, where each pigment subtracts light and mixing everything heads toward muddy black (the CMYK model printers use). Screens add, pigments subtract — keep that split straight and RGB stops feeling arbitrary.
Each channel is graded on 0 to 255 — 256 steps, because one channel is stored in a single 8-bit byte (28 = 256 values). So rgb(0, 0, 0) is black, rgb(255, 255, 255) is white, and rgb(255, 0, 0) is the reddest red the format can name. Three 8-bit channels give 2563 ≈ 16.7 million colors, the familiar "24-bit true color." When all three channels are equal — rgb(128, 128, 128) — you get a neutral grey, a handy sanity check: unequal channels mean the color has a tint.
HEX: RGB written in base-16
A HEX color is not a different model at all — it is the same three RGB numbers, just written in hexadecimal (base-16) and glued together after a #. Hexadecimal uses sixteen digits, 0–9 then a–f (where a=10 … f=15), and the neat thing is that one byte (0–255) always fits in exactly two hex digits: 00 is 0 and ff is 255. So the six characters of #RRGGBB are three pairs — red, green, blue — each a two-digit base-16 number. #ff0000 is rgb(255, 0, 0); #3366cc is rgb(51, 102, 204), which we work out digit by digit below.
There is also a three-digit shorthand, #RGB, where each single digit is doubled to form its channel. So #f0c expands by copying every nibble: f→ff, 0→00, c→cc, giving #ff00cc — not #0f0c00. This only works for colors whose channels are repeated digits (00, 11, … ff). #3366cc qualifies and can be written #36c; a color like #3467cd cannot, because 34, 67 and cd are not doubled. Confusing the shorthand rule (doubling vs. zero-padding) is one of the most common hand-editing mistakes.
HSL: the notation built for humans
RGB and HEX are honest about the hardware but awkward for a person. Quick — to make rgb(51, 102, 204) a bit lighter, which numbers move, and by how much? It is not obvious, because lightness is smeared across all three channels. HSL fixes that by re-describing the color along three axes a human actually thinks in:
- Hue — an angle from
0to360°around a color wheel: 0° red, 120° green, 240° blue, wrapping back to red. It answers "which color?" - Saturation —
0%to100%, how vivid versus how grey. 0% is a pure grey regardless of hue; 100% is fully vivid. - Lightness —
0%to100%, from black through the pure color at 50% up to white at 100%.
Now the edits are obvious: to lighten a color you raise the L; to derive a hover state you nudge L by a few percent; to make a muted variant you drop the S; to build a complementary color you add 180° to the H. That is why designers reach for HSL when deriving a palette from one base color — the change you want maps to a single number. The same color as RGB gives you three numbers that must all move together to do the same job.
Alpha: adding transparency
All three notations gain a fourth value for opacity — the alpha channel — which controls how much of the color shows versus how much of whatever sits behind it shows through. In rgba() and hsla() alpha is a fraction from 0 (fully transparent) to 1 (fully opaque): rgba(51, 102, 204, 0.5) is our blue at half strength, letting the background bleed through. HEX gets an eight-digit form, #RRGGBBAA, where alpha is a fourth pair of hex digits on the same 0–255 scale as the color channels. So ff is fully opaque, 00 is invisible, and 80 is about 50% because 128 / 255 ≈ 0.502. Our blue at half opacity is #3366cc80. The one gotcha: alpha in rgba() is a 0–1 fraction, but alpha in hex is a 0–255 pair — the same concept on two different scales.
Worked example: #3366cc across all three
Let's move one concrete color, #3366cc, through HEX → RGB → HSL by hand. First, HEX to RGB — read each pair as base-16:
Now RGB to HSL. Scale each channel to the 0–1 range by dividing by 255, then find the largest and smallest:
So the identical blue is #3366cc = rgb(51, 102, 204) = hsl(220, 60%, 50%). The three swatches below are all this one color — proof the notations agree:
Reading the HSL tells you something the HEX hides: hue 220° puts it firmly in the blue band, 60% saturation says moderately vivid (not neon, not grey), and 50% lightness says it is a true mid-tone — exactly on the black-to-white midpoint. To turn it into a pale background you would keep 220° and 60% and just raise L toward, say, 92%; the RGB and HEX for that lighter shade would look unrelated at a glance, but in HSL it is one nudge.
The honest catch: it is all sRGB
Here is the limit worth knowing. When you write plain #3366cc, rgb() or hsl() in CSS or a design tool, the numbers are interpreted in the sRGB color space — a standard from 1996 that ordinary monitors were built to match. sRGB defines a specific gamut: the finite range of colors those three channels can actually reach. Anything outside that triangle of reachable colors simply cannot be named with plain RGB/HEX/HSL, no matter what numbers you pick.
Modern wide-gamut displays (Apple's Display-P3 and similar) can show noticeably more saturated reds and greens than sRGB covers — the P3 gamut is roughly 25% larger by area. To reach those extra colors, CSS added notations like color(display-p3 1 0 0) and the perceptually-designed oklch(); a plain hex code can never address them. One more honest caveat: HSL is not perceptually uniform — hsl(60, 100%, 50%) (yellow) looks far brighter to the eye than hsl(240, 100%, 50%) (blue) despite both claiming 50% lightness, so equal L values are not equally bright. For everyday web and UI work, though, sRGB with HEX, RGB and HSL is the correct, portable default — reach for the wide-gamut notations only when you need colors sRGB can't hold.
Pick any color and watch its HEX, RGB, RGBA, HSL, HSLA and HSV forms stay in sync as you drag a slider — plus a WCAG contrast check and tint/shade swatches. Runs entirely in your browser; nothing is uploaded.
Open the color converter → to convert any color between HEX, RGB and HSL live, or browse all Polyatic tools.