utilime

Small tools that finish the job.

Convert hex to RGB, and any CSS notation to all the others

One field takes any CSS colour notation and hands back all the others, with the round trip checked so a lossy conversion says so.

Drop a file hereor click to choose · it stays on your device

How it works

A hex colour is three 8-bit integers written in base 16. The string #4a7ba7 carries 74, 123 and 167, and carries nothing else: no colour space is named in it, no white point, no viewing condition. The convention that fills those gaps is sRGB, and the part of that standard people skip is the encoding. Those three numbers are not quantities of light. Each one has already been bent by a transfer curve so a display can take it directly.

The curve is the reason a value halfway up the byte range sits nowhere near halfway up the light. Undoing it means dividing by 255 and raising to the power 2.4 over a 1.055 and 0.055 offset, with a short linear segment below 0.04045. Push 128 through that and the relative luminance of #808080 comes out at 0.2159, measured here: the grey emits under 22 percent of the light #ffffff does. The byte that genuinely halves white is #bcbcbc, at 0.5029. Halving a colour's numbers does something other than halving its brightness.

Three digits, four digits, eight digits

Shorthand hex repeats each digit rather than shifting it left. #abc expands to #aabbcc, a multiplication by 17, so #fff arrives at 255 exactly; shifting four bits would multiply by 16 and stop white at 240. The cost is reach. Three digits address 16 values per channel, which is 4,096 colours against the 16,777,216 the six-digit form can name, and nothing between two shorthand steps can be written at all. Squeezing a full colour into three digits is a quantisation rather than an abbreviation.

CSS Color 4 added the four- and eight-digit forms, which append an alpha byte to the same string. That byte does not line up with the alpha rgba() takes. Hex alpha has 256 steps; rgba() alpha is a fraction or a percentage. Half opacity in hex is either 7f, which is 0.498, or 80, which is 0.502, and neither is 0.5. A token converted from rgba(0 0 0 / 50%) into #00000080 has moved, and converting it back reports 50.2 percent. The shift is invisible on screen and survives every conversion after it.

HSL and HWB are the same wheel

HSL's lightness is the midpoint between the largest and smallest channel, computed on the encoded bytes rather than on light. Yellow at hsl(60 100% 50%) and blue at hsl(240 100% 50%) claim identical lightness while measuring 0.9278 and 0.0722 in relative luminance, a ratio of nearly 13 to 1. HWB reads the same hue angle and then describes the colour as pigment mixed with white and black: whiteness is the smallest channel, blackness is one minus the largest. Where those two sum past 100 percent the hue is discarded and the answer is grey in their ratio.

For a space whose lightness tracks what an eye reports, oklch() is the one to reach for, and the palette tool covers how it is built. It earns its row here for a duller reason: of the notations on this page it is the only one whose numbers stay meaningful while a browser interpolates or lightens the colour.

Where the round trip breaks

Whole-number hsl() cannot address 8-bit sRGB. Integer hue gives 361 values and integer saturation and lightness give 101 each, so the notation has roughly 3.68 million states for 16,777,216 colours, and the shortfall is settled before rounding is even considered. Sweeping the entire cube for this page: 15,001,704 colours out of 16,777,216 fail to come back from whole-number hsl(), which is 89.4 percent of them, and whole-number hwb() fails for 15,406,684. A single decimal place repairs both, with no failures anywhere in the cube.

So the page converts out and parses back on every keystroke, and reports the outcome as a row. At the default setting #4a7ba7 leaves as hsl(208, 39%, 47%) and returns as #497ba7, one level down in red. That is invisible on screen and still wrong in a file, and it is the drift that accumulates when a colour is passed between tools. Raising the decimal places control repairs it, and the row updates to say so.

Naming a colour

CSS has 148 colour names covering 139 distinct colours, since seven gray and grey spellings are duplicates, aqua matches cyan, and fuchsia matches magenta. The keyword transparent is not among them; it resolves to a black with zero alpha, which is worth knowing before fading anything through it. The nearest-name row measures distance in OKLab, a space where black to white is exactly 1 and two neighbouring mid greys sit 0.0034 apart. #4a7ba7 is 0.0245 from steelblue, close enough to see they are related and far enough to see they differ.

Questions

Why does the hsl() I copied not give me back my hex?

Because at whole numbers the notation has fewer states than sRGB has colours: about 3.68 million against 16,777,216. Measured across the full cube, 89.4 percent of colours return a different byte after a trip through integer hsl(). The round-trip row names the notation that lost and shows what came back. Setting the decimal places control to 1 makes hsl() and hwb() exact for every colour there is.

Which notation should go in a stylesheet?

Hex for a fixed value: it is the shortest exact form and every renderer has understood it for thirty years. oklch() for anything a browser will interpolate or lighten, since a transition through it stays the colour it started as. hsl() is pleasant to nudge a hue by hand and worth converting away from before the value is stored, because that is where the rounding gets baked in.

What can I paste into the box?

Hex in all four lengths with or without the hash, any CSS colour name, transparent, and the functional forms rgb(), rgba(), hsl(), hsla(), hwb(), oklch(), oklab() and lab(). Channels may be numbers or percentages, hue may carry deg, rad, grad or turn, and alpha may follow a slash or a comma. Half-finished input is read as far as it parses instead of being thrown away.

Is #808080 the middle grey?

It is the middle byte, which is a different claim. Its relative luminance is 0.2159, so it emits about 21.6 percent of the light white does. The byte that halves white is #bcbcbc. Both figures fall out of the sRGB transfer curve, which spends more of the byte range on dark values because human vision discriminates better down there.

What happens to alpha?

It is carried through every notation that can hold it, and shown as an eight-digit hex whenever it is below 1. The two representations disagree by construction: hex alpha is one of 256 steps while rgba() alpha is a fraction, so a requested 0.5 becomes the byte 80 and reads back as 0.502. The row says so when the value you asked for is not one the byte can hold.