Hex to RGB Converter — with HSL, Relative Luminance and WCAG Contrast
Hex to RGB Converter — with HSL, Relative Luminance and WCAG Contrast
Paste a hex colour and get R, G and B, the HSL triple, the sRGB relative luminance, and the WCAG 2 contrast ratio against white and against black — with the AA and AAA thresholds for normal and large text shown as bands. Three-digit shorthand and a missing hash are both accepted.
Hex colour to RGB, HSL and contrast
#000000, plain black, measured against white — the page default, and the largest ratio the formula can return.
sRGB relative luminance and the contrast ratio
Rlin = Rs ÷ 12.92 if Rs ≤ 0.04045, else ((Rs + 0.055) ÷ 1.055)2.4
L = 0.2126 Rlin + 0.7152 Glin + 0.0722 Blin
contrast = (L1 + 0.05) ÷ (L2 + 0.05), L1 the lighter of the two
- Rs, Gs, Bs
- the channels scaled to 0 to 1. This division is the step that matters: the 0.04045 threshold on the next line is a threshold on THIS value, not on the 0 to 255 one. Compared against the 0 to 255 value it corresponds to 10.31475, so channels 0 to 10 take the linear branch and 11 to 255 take the power branch
- 12.92 and 2.4
- the two halves of the sRGB electro-optical transfer function: a short linear segment near black, then a power curve. The linear segment exists so the curve has a finite slope at zero
- 0.2126, 0.7152, 0.0722
- the luminance weights of the sRGB primaries. Green carries 71.52% of the luminance and blue only 7.22%, which is why two colours can share an HSL lightness and still differ in contrast
- 0.05
- the flare term added to both luminances. It is what stops the ratio going to infinity against pure black and what caps the whole scale at 21:1, because (1 + 0.05) ÷ (0 + 0.05) = 21 exactly
Worked example
#000000, plain black, measured against white — the page default, and the largest ratio the formula can return.
All three channels are 0, so each scaled channel is 0.0, which is at or below the 0.04045 threshold: the linear branch applies and 0 ÷ 12.92 = 0
L for black = 0.2126×0 + 0.7152×0 + 0.0722×0 = 0. L for white = every channel 1.0, above the threshold, so ((1 + 0.055) ÷ 1.055)2.4 = 12.4 = 1 in each channel, and the weights sum to 0.2126 + 0.7152 + 0.0722 = 1 exactly
contrast = (1 + 0.05) ÷ (0 + 0.05) = 1.05 ÷ 0.05 = 21. Not 21-ish: exactly 21, which is why it is the standard check on any implementation of this formula
Now the same arithmetic for #767676 — grey 118 — to show the other branch. 118 ÷ 255 = 0.462745, which is above 0.04045, so ((0.462745 + 0.055) ÷ 1.055)2.4 = 0.4907542.4 = 0.181164 in all three channels, and L = 0.181164 because the weights sum to 1
Against white that is 1.05 ÷ 0.231164 = 4.54, which clears 4.5:1 — and grey 119, #777777, gives 4.48 and does not. That one-step difference between #767676 and #777777 is the most quoted worked example of this formula, and reproducing both is how this page was checked
Published worked examples, recomputed here
| Colour | R, G, B | Relative luminance L | Contrast vs white | Contrast vs black | Widely published figure |
|---|---|---|---|---|---|
| #000000 black | 0, 0, 0 | 0.000000 | 21.00 | 1.00 | 21:1 — the maximum |
| #FFFFFF white | 255, 255, 255 | 1.000000 | 1.00 | 21.00 | 21:1 against black |
| #FF0000 red | 255, 0, 0 | 0.212600 | 4.00 | 5.25 | 3.99 or 4.0 vs white |
| #00FF00 green | 0, 255, 0 | 0.715200 | 1.37 | 15.30 | 1.37 vs white |
| #0000FF blue | 0, 0, 255 | 0.072200 | 8.59 | 2.44 | 8.59 vs white |
| #808080 grey 128 | 128, 128, 128 | 0.215861 | 3.95 | 5.32 | 3.95 vs white |
| #767676 grey 118 | 118, 118, 118 | 0.181164 | 4.54 | 4.62 | 4.54 — the lightest grey passing AA on white |
| #777777 grey 119 | 119, 119, 119 | 0.184475 | 4.48 | 4.69 | 4.48 — one step lighter, and it fails |
| #595959 grey 89 | 89, 89, 89 | 0.099899 | 7.00 | 3.00 | 7.00 — just over the AAA line |
What the thresholds are, by success criterion
| Requirement | Success criterion | Level | Ratio needed |
|---|---|---|---|
| Normal text | 1.4.3 Contrast (Minimum) | AA | 4.5:1 |
| Large text — at least 18 point, or 14 point bold | 1.4.3 Contrast (Minimum) | AA | 3:1 |
| Normal text | 1.4.6 Contrast (Enhanced) | AAA | 7:1 |
| Large text — at least 18 point, or 14 point bold | 1.4.6 Contrast (Enhanced) | AAA | 4.5:1 |
| User-interface components and graphical objects | 1.4.11 Non-text Contrast | AA | 3:1 |
Where the sRGB curve bends, channel by channel
| 8-bit channel | Scaled value | Branch taken | Linearised value |
|---|---|---|---|
| 0 | 0.000000 | linear, value divided by 12.92 | 0.00000000 |
| 1 | 0.003922 | linear | 0.00030353 |
| 5 | 0.019608 | linear | 0.00151763 |
| 10 | 0.039216 | linear — the last channel on this branch | 0.00303527 |
| 11 | 0.043137 | power, 2.4 exponent — the first channel on this branch | 0.00334654 |
| 64 | 0.250980 | power | 0.05126946 |
| 128 | 0.501961 | power | 0.21586050 |
| 255 | 1.000000 | power | 1.00000000 |
What the numbers mean, and which one to trust
A hex colour is three bytes, and that is all it is. #1A2B3C is 1A for red, 2B for green and 3C for blue — 26, 43 and 60 in decimal, because each pair of hex digits is exactly one byte and one byte runs 0 to 255. The three-digit shorthand #abc is defined to mean #aabbcc: each digit is doubled, not padded with zeros, so #abc is not #0a0b0c. That is why only three-digit and six-digit forms exist and #12345 is meaningless. Read as a single number the whole colour is a 24-bit integer from 0 to 16,777,215, which is where the phrase 16.7 million colours comes from.
HSL is a different description of the same three bytes, not extra information. Hue, saturation and lightness are computed from the largest and smallest of the three channels, and the transformation is exactly reversible. It is useful for picking a related colour — the same hue, less saturated — and it is actively misleading for accessibility, because HSL lightness weights the three channels equally and human vision does not. Pure blue and pure yellow both have an HSL lightness of 50%, and their relative luminances are 0.0722 and 0.9278: a factor of thirteen apart.
Relative luminance is the number that accessibility rests on. WCAG 2 defines it in two steps. First undo the sRGB transfer curve on each channel: divide the channel by 255, then either divide by 12.92 or raise to the power 2.4, depending on a threshold at 0.04045. Then weight the three linear values by 0.2126, 0.7152 and 0.0722 and add them. Those weights are why green dominates and blue barely counts. The threshold is the step that gets implemented wrongly most often, because it is a threshold on the scaled 0 to 1 channel and the value sitting in front of you is usually the 0 to 255 one; on that scale the same threshold is 10.31475.
The contrast ratio is then (L1 + 0.05) divided by (L2 + 0.05), with the lighter colour on top. The 0.05 is a flare term standing in for light reflecting off the screen, and it is what makes the scale finite: without it, any colour against pure black would be infinite. With it, the range is exactly 1:1 for two identical colours to 21:1 for black against white, and nothing can exceed 21. A consequence worth internalising: the ratio against white and the ratio against black are not independent. Multiply them and you get 21 divided by (L + 0.05), so a colour that contrasts beautifully with white necessarily contrasts poorly with black. There is no colour that is comfortable on both: since the product is fixed at 21, neither ratio can beat the square root of 21, 4.5826:1, while the other keeps up. The band that clears 4.5:1 on each is a luminance window from 0.175 to 0.18333, which among the 256 greys admits exactly two — 757575 and 767676.
What this page does not do. It compares your colour against white and against black, not against an arbitrary second colour, because those two are the cases a single-value calculator can answer and they bracket every other answer. It says nothing about colour blindness, which contrast ratio does not measure. And it uses WCAG 2’s formula rather than APCA, the perceptual replacement drafted for WCAG 3: APCA gives different and in some cases better answers, but AA and AAA conformance is still defined by the formula above, so that is the one implemented here.
Frequently asked questions
How do I convert a hex colour to RGB by hand?
Split the six digits into three pairs and read each pair as a base-16 number: the first digit is worth sixteen times its face value and the second its face value, where A is 10 and F is 15. So 1A is 1×16 + 10 = 26, 2B is 2×16 + 11 = 43, and 3C is 3×16 + 12 = 60, giving rgb(26, 43, 60). The three-digit shorthand doubles each digit first: #abc means #aabbcc, so the channels are 0xAA = 170, 0xBB = 187 and 0xCC = 204. Going back the other way, each channel from 0 to 255 becomes exactly two hex digits, padded with a leading zero if needed — 5 becomes 05, not 5.
What contrast ratio do I actually need?
4.5:1 for body text and 3:1 for large text to meet WCAG 2 Level AA — SC 1.4.3 — where large means at least 18 point, or at least 14 point bold. For Level AAA, SC 1.4.6, it is 7:1 for body text and 4.5:1 for large. Icons, form field borders and focus indicators need 3:1 under SC 1.4.11. Each of those is a minimum, so a ratio of exactly 4.5:1 passes. AA is what almost every legal accessibility requirement points at, which makes 4.5:1 the number to design against and 3:1 the concession for headings.
Why is the maximum contrast ratio 21 and not something rounder?
Because of the 0.05 flare term. White has a relative luminance of exactly 1 and black exactly 0, so the ratio is (1 + 0.05) divided by (0 + 0.05), which is 1.05 divided by 0.05 = 21 exactly. Change the flare term and the ceiling changes; it exists so that the formula does not divide by zero against pure black, and so that two very dark colours are not treated as wildly different just because one is marginally less dark. It is also the reason the ratio against white and the ratio against black cannot both be large: their product is 21 divided by (L + 0.05), which is at most 21.
My contrast checker gives a slightly different number. Which is right?
Check the second decimal place before worrying. Most disagreements are rounding: 3.99 against 4.0 for pure red on white is the same number shown to different precision. A real disagreement of a few per cent on dark colours usually means the sRGB threshold is being tested against the 0 to 255 channel rather than the scaled 0 to 1 one — an error that only affects channels 1 to 10 and at its worst moves a ratio by 0.28, at rgb(1,1,1). A disagreement of several whole units means something bigger, most often that the transfer curve has been skipped entirely and the raw channels have been averaged. WCAG 2.0 wrote the threshold as 0.03928 and WCAG 2.1 and 2.2 write it as 0.04045; for 8-bit channels those two give identical results for all 256 values, so that is never the explanation.
Can I use HSL lightness instead of relative luminance to judge contrast?
No, and it is a common and expensive mistake. HSL lightness is the midpoint of the largest and smallest channel, which treats red, green and blue as equally bright. Relative luminance weights them 0.2126, 0.7152 and 0.0722, because the eye is far more sensitive to green. Pure blue and pure yellow both have HSL lightness 50%; their luminances are 0.0722 and 0.9278, and their contrast ratios against white are 8.59:1 and 1.07:1. One passes AAA comfortably and the other is nearly invisible. Use HSL to choose a colour and luminance to check it.
What is APCA, and should I be using it instead?
APCA — the Accessible Perceptual Contrast Algorithm — is a newer contrast model drafted in connection with WCAG 3, and it addresses real weaknesses in the WCAG 2 formula, particularly for light text on dark backgrounds and for very dark colour pairs, where the WCAG 2 ratio is known to be optimistic. It is not a drop-in replacement: its output is a lightness difference on a different scale, not a ratio, and its thresholds are tied to font size and weight together. As things stand, WCAG 2.1 and 2.2 Level AA and AAA conformance is defined by the ratio this page computes, so that is what is implemented here. If you are designing dark themes, it is worth checking both.
Related calculators
References
- W3C, Web Content Accessibility Guidelines (WCAG) 2.2, W3C Recommendation. The two definitions implemented here are relative luminance — divide each 8-bit channel by 255, then take the channel divided by 12.92 when the scaled value is at or below 0.04045 and ((scaled + 0.055) ÷ 1.055)2.4 otherwise, then weight by 0.2126, 0.7152 and 0.0722 — and contrast ratio, (L1 + 0.05) ÷ (L2 + 0.05) with L1 the lighter. The thresholds are from SC 1.4.3 Contrast (Minimum), Level AA (4.5:1 normal, 3:1 large), SC 1.4.6 Contrast (Enhanced), Level AAA (7:1 normal, 4.5:1 large), and SC 1.4.11 Non-text Contrast, Level AA (3:1); large text is defined as at least 18 point or 14 point bold. The Recommendation is copyrighted: it is cited by clause, the formula is implemented rather than quoted at length, and none of its tables or technique pages are reproduced. It was NOT fetched in this session — no outbound network access was available — so the wording above is from knowledge and every number in it was checked by computation instead. WCAG 2.0 states the threshold as 0.03928 and WCAG 2.1 and 2.2 as 0.04045; for all 256 8-bit channel values the two give bit-identical results, which was verified here.
- IEC 61966-2-1:1999 (sRGB) is the origin of the 12.92 / 2.4 piecewise transfer function and of the primaries whose luminance coefficients are 0.2126, 0.7152 and 0.0722. The standard is paywalled and was NOT read. Nothing from it is reproduced; it is named only to say where the constants in the WCAG formula come from, and the constants themselves are taken from the WCAG definition above.
- The nine colours in the first table are the pairs every public contrast checker publishes figures for, and the expected values in the last column are those widely published figures, quoted from knowledge and not re-fetched in this session. This implementation agrees with all nine to the second decimal place. Two of the nine are exact consequences of the formula and need no external source at all: black against white is 1.05 ÷ 0.05 = 21, and pure red against white is 1.05 ÷ 0.2626, because a saturated red channel contributes its weight 0.2126 and the other two contribute nothing.
- Verification done for this page, in the engine that runs it. The channel split and the full HSL conversion were checked against an independent implementation for all 16,777,216 sRGB colours with zero disagreements; the luminance and both contrast ratios were checked against an independent implementation for 2,396,746 sampled colours and agreed to the last bit of a double (largest difference 0.0). The size of the mis-scaled-threshold error quoted in the third table’s caption was measured by evaluating both implementations over all 16,777,216 colours: the largest luminance difference is 0.00068015 at rgb(1,1,1) and the largest contrast-against-white difference is 0.278461, from 20.873 to 20.595.
Unit definitions vary by region and manufacturer. Where a spread exists we show it. For food safety, follow your national authority's current guidance. Full disclaimer at calcengines.com/disclaimer/
