Skip to content
ColorKit

Free hsl to rgb converter

HSL to RGB for targets that cannot take HSL

Free, no signup: move hue, saturation, lightness and alpha — or paste a declaration with the hue in degrees, radians, gradians or turns — and the color comes back in six shapes, from rgb() and hex through the unrounded channels to 0-1 float triples in both sRGB-encoded and linear-light form. The table beside the sliders marks which of the six 60° arcs your hue fell into and prints the chroma, second-channel and match values that produced the answer, so the result is traceable rather than asserted. Hue wraps instead of clamping, which is what a rotation of −30° or 400° needs.

  • 100% free
  • No signup
  • 4 hue units accepted
  • 6 output shapes
  • Linear-light triple for shaders

rgb(64, 191, 64)

hsl(120, 50%, 50%)

120
50%
50%
100%

Press Ctrl+V anywhere on the page to drop in an hsl() declaration — no need to click the field first.

Channel table for hue 120°

C
0.5000
(1 − |2L − 1|) × S
X
0.0000
C × (1 − |(H ÷ 60) mod 2 − 1|)
m
0.2500
L − C ÷ 2
The six hue sectors and the channel pattern each one uses
ArcR G B before mRuns from
0°–60°C X 0red to yellow
60°–120°X C 0yellow to green
120°–180°active0 C Xgreen to cyan
180°–240°0 X Ccyan to blue
240°–300°X 0 Cblue to magenta
300°–360°C 0 Xmagenta to red

The active row gives R 0, G 127.5, B 0 on 0-255 before m is added back to all three.

The same color, shaped for six targets

  • CSS rgb()
  • CSS Color 4 slash form
  • Six-digit hex
  • Unrounded channels
  • 0-1 floats, sRGB-encoded
  • 0-1 floats, linear light

Two of those six are 0-1 triples and they are not interchangeable: the sRGB-encoded one is what a texture or a CSS value holds, the linear one is what lighting arithmetic in a shader has to work on.

How to convert HSL to RGB

Set the triple, see which arc it lands in, then take the output in the shape your target reads.

  1. Set the triple in whichever unit you have it

    Hue runs 0-360 on the slider and the dropdown restates it in radians, gradians or turns. The text field goes further and parses all four on the way in, so hsl(0.5turn 50% 50%), hsl(3.1416rad 50% 50%) and hsl(200grad 50% 50%) all land on the same cyan as hsl(180 50% 50%). Saturation, lightness and alpha are ordinary percentages.

  2. See which arc the hue fell into

    The table marks the active one of the six 60° arcs and prints the three quantities the algorithm builds: chroma C, the second channel X, and the match value m added to all three at the end. At 120° the pattern reads 0, C, X — green takes the full chroma, red and blue start from the floor — and the line under the table gives those channels on 0-255 before m goes back in.

  3. Copy the shape your target actually needs

    Six outputs sit under the table. rgb() and hex go into a stylesheet or a template field; the unrounded channels go into anything that will do further arithmetic before it rounds; the sRGB-encoded 0-1 triple goes into a shader uniform that only passes the color through; and the linear-light triple goes into one that multiplies it by something. Each has its own copy button so you never trim a string by hand.

Technical specifications

Hue input0-360 on the slider; the text field also reads rad, grad and turn — 0.5turn, 3.1416rad and 200grad all resolve to 180°
Hue wrapValues outside the range wrap instead of clamping: 400° becomes 40° and −30° becomes 330°, so a rotation never sticks at an end
Saturation and lightnessBoth clamp to 0-100 rather than wrapping, unlike hue — a pasted 140% is pulled back to 100, not folded round to 40
Channel tableSix rows, one per 60° arc, the active row marked, with C, X and m printed to 4 decimal places for the current color
Unrounded channelsTwo decimal places — hsl(120, 50%, 50%) is 63.75, 191.25, 63.75 before an 8-bit surface rounds it to 64, 191, 64
Output shapesSix — rgb(), the slash-alpha spelling, six- or eight-digit hex, the unrounded channels, and 0-1 triples in sRGB-encoded and linear-light form
LinearizationThe sRGB transfer function: c ÷ 12.92 at or below 0.04045, and ((c + 0.055) ÷ 1.055) raised to 2.4 above it
Where the outputs come fromAll six are computed in the page from your three numbers — no lookup table, no rounding done elsewhere and no request

Frequently asked questions

hsl(120, 50%, 50%) shows as rgb(64, 191, 64) and also as 63.75, 191.25, 63.75 — which is right?

Both, at different stages. The formula produces fractional channels, an 8-bit surface cannot store them, so the browser rounds at the moment of painting and getComputedStyle reports rgb(64, 191, 64). The unrounded line exists because that fraction is worth keeping while the value is still on its way through further arithmetic — a gradient, a blend, an average of several colors — where rounding at every step accumulates into a visible shift. Round once, at the end.

Can I write the hue in radians or turns?

Yes, and in gradians as well. CSS Color 4 types the first component of hsl() as an angle, so 180deg, 0.5turn, 3.1416rad and 200grad all name the same hue and a bare number is read as degrees. It matters most in generated code: a rotation computed in radians can go straight into the declaration with no conversion step, and the unit dropdown here restates your current hue in whichever unit the source you are working from uses.

What happens to a hue below 0 or above 360?

It wraps, so 400° comes out as 40° and −30° as 330°. This is in the standard rather than a leniency of this page: hue is a position on a circle, a rotation that carries past the end has an obvious meaning, and clamping at 360 the way a percentage clamps at 100 would break every hue-rotation loop ever written. Saturation and lightness do clamp, because they are ratios along a finite axis rather than angles.

What are C, X and m in the channel table?

They are the three quantities the algorithm builds before it assigns anything to a channel. C is chroma, the full width the color can have at that lightness, worked out as (1 − |2L − 1|) × S; X is the strength of the second-strongest channel and depends on how far into its 60° arc the hue sits; m is the match value, L − C ÷ 2, added to all three channels at the end to lift the trio to the lightness you asked for. For hsl(120, 50%, 50%), C is 0.5, X is 0 because 120° sits exactly on an arc boundary, and m is 0.25.

For a WebGL shader, which of the two float triples do I use?

The sRGB-encoded one if the shader only passes the color through to the screen, the linear one the moment it multiplies the color by anything. Textures and CSS values hold gamma-encoded numbers, and multiplying two gamma-encoded values — a light term, an alpha blend, a mix — produces a result that is wrong in a way that reads as muddy midtones and grey seams in gradients. Convert to linear, do the arithmetic, convert back at the end; the linear line here has already applied the transfer function, so it can go straight into a uniform.

Which of the six outputs carry the alpha value?

Three of them: the legacy line, the Color 4 slash line and the hex output. Below 100% the legacy line becomes rgba() with alpha as a decimal, the slash line writes it as a percentage after the channels, and the hex output grows a fourth pair. The other three are channels only — the unrounded triple and both float triples are color data on its way into a buffer or a uniform, where alpha travels as a separate component rather than inside the string.

Why is a 50% lightness grey not exactly rgb(128, 128, 128)?

Because the exact answer is 127.5 on all three channels and eight bits cannot hold a half. With saturation at zero the chroma is zero too, so every channel equals the match value, and at 50% lightness the match value is precisely half of 255. That leaves 128 as the nearest storable grey, sitting a fraction lighter than the midpoint it stands in for, and 127 as the nearest one below — which is why two greys that ought to be identical sometimes differ by one depending on which tool rounded them.

About resolving HSL back to channels

HSL is a notation, not a storage format. Nothing downstream of a stylesheet holds a hue: the engine resolves the declaration into three sRGB channels before it paints a single pixel, and this page performs that same resolution where you can watch it. The resolution runs in three moves — work out the chroma the requested lightness allows, hand C, X and 0 to red, green and blue according to which 60° arc the hue occupies, then add the match value to all three so the trio arrives at the lightness asked for. The six arcs are not an implementation detail either: they are the reason the primaries and secondaries land on exact multiples of 60°, and why the named codes worth memorising cluster where they do.

The output most converters give you is a rounded rgb() string and nothing else, and that is a real loss in two directions. Downward, because the fractional channels are the correct input to any further arithmetic — blend, average, gradient stop — and rounding at each step compounds; upward, because a 0-1 float triple is what a shader uniform, an ImageData byte array or a native mobile color API actually wants, and hand-dividing three numbers by 255 is where a typo lives. The linear-light triple is the one almost nobody offers: gamma-encoded values are correct to display and wrong to multiply, so a color destined for lighting maths has to be linearized first or the midtones go muddy. When the channels are heading to ink rather than to a screen, the RGB to CMYK page takes them on and explains what an ICC profile changes.

One more thing the angle buys you: because hue is a position on a circle, arithmetic on it stays meaningful past the ends, so 400° is simply 40° and a loop that adds 15° a frame never needs a modulo. Saturation and lightness get no such treatment, since a ratio past 100% has nowhere to go, and this page clamps them the way a browser does. To come the other way, start at RGB to HSL if you are holding channels or hex to HSL if you are holding a code, and use the color wheel when the question is which other angles to pair yours with.

Where this conversion runs

Every number on this page is worked out by JavaScript running in the tab you are reading it in. Nothing you type, paste or open is uploaded, logged or kept, which is also why the tools carry on working after you disconnect from the network.

Nor is there anything to send: this page takes three numbers you already have and does arithmetic on them, so there is no file, no image and no pasted document involved at any point.