HSL to HEX Converter_

Convert hsl to hex as you type — comma or space syntax, hsla(), bare triples, and hue in deg, rad, grad or turn. Drag the three sliders instead if you have no value yet. That direction is exact: every HSL colour has one hex code and nothing is lost getting there.

What this page adds is the part that trips people up. Alongside the hex you get the light the colour actually emits, so you can see that hsl(60, 100%, 50%) and hsl(240, 100%, 50%) claim the same lightness and differ by nearly thirteen times — the reason palettes built by holding L steady come out uneven.

toolkit.codes/hsl-to-hex
Enter an HSL colour to convert it
UTF-8
Ready
100% LOCAL
Input
A hue from 0 to 360 and two percentages, typed as hsl(), pasted bare, or dragged on three sliders.
Output
The hex code, plus the measured luminance, the oklch() equivalent, and the HSB reading of the same three numbers.
Processing
Converted in this tab on every keystroke. HSL to RGB is exact arithmetic; the luminance figure is the WCAG relative-luminance formula, not an estimate.
Limits
Whole-number HSL can only name 2,037,745 of the 16,777,216 colours a hex can hold — about 12.1%. So this direction is exact and the reverse is not.
L is not brightness
HSL’s lightness is the midpoint between the strongest and weakest channel. It has no model of the eye in it, which is why a yellow and a blue at the same L look nothing alike.

The L in HSL is not brightness

Two colours, one lightness, thirteen times the light

hsl(60, 100%, 50%) is a pure yellow and hsl(240, 100%, 50%) is a pure blue. Both report 50% lightness. Measured with the standard relative-luminance formula, the yellow emits 92.8% of what a white pixel would and the blue emits 7.2% — a ratio of 12.85 to one. Neither number is wrong, because L was never a brightness measurement. It is (max + min) / 2 across the three channels: for the yellow that is (255 + 0) / 2, and for the blue it is (255 + 0) / 2 as well. Identical arithmetic, identical answer, and the eye disagrees completely.

Why the palette you built looks lumpy

The usual recipe for a colour scale is to fix saturation and lightness and step the hue. Tidy, brief, and it yields a set where the yellows glare and the blues sink — because holding L constant holds nothing perceptual constant. The hue walk under every conversion above shows the effect at whatever S and L you have set: same claimed lightness across six hues, luminance spanning an order of magnitude. Designers usually conclude they chose badly. The tool chose for them.

OKLCH is the version of this that works

oklch() has the same shape — lightness, chroma, hue — and its lightness genuinely tracks the eye, so two colours with the same L do look equally bright. The readout above gives the OKLCH form of whatever you convert. Run the yellow and the blue through it and the numbers stop lying: 96.8% against 45.2%. Every current browser parses oklch() natively, so a palette can be authored in it directly. The trade is that not every OKLCH triple exists in sRGB — push chroma up at a lightness that cannot support it and the browser clamps.

HSL and HSB are not the same space

Figma, Photoshop and Sketch show HSB; CSS takes HSL. They share the hue and disagree about everything after it, so numbers copied from a design tool into an hsl() function land somewhere else entirely. hsb(210, 100%, 100%) is a vivid blue; type those same digits as hsl(210, 100%, 100%) and you get #ffffff. Pure white. The HSB row above shows what your value would read as in a design tool, which is the number to check against before blaming the handoff.

Type it, drag it, or read what it costs you

  1. 01Drop the colour in however it reached you — hsl(217, 91%, 60%), the modern space syntax, a bare 217 91 60, or hsla whose alpha this page ignores. Conversion runs on every keystroke.
  2. 02No value yet? Drag the three sliders. They and the text field drive each other, so you can land roughly by eye and then finish numerically.
  3. 03Take the hex from the top, uppercased if your linter wants it. That direction is exact — there is no rounding to worry about on the way out.
  4. 04Read the rows below it when the colour is going into a palette: what it actually emits, the oklch() equivalent to author in instead, and what the same digits mean in a design tool.

Four colours coming out of HSL

A design token into a stylesheet

The system is authored in HSL; the build output wants hex.

Token
hsl(217, 91%, 60%)
CSS
--brand: #3c83f6;

Numbers copied out of Figma

The panel said 210 / 100 / 100 — but that panel is HSB.

As hsl()
hsl(210, 100%, 100%) → #ffffff
As hsb()
hsb(210, 100%, 100%) → #0080ff

Checking a scale before shipping it

Six hues at one lightness, and the yellow looks wrong.

Both claim
50% lightness
Actually emit
92.8% and 7.2%

A hue in turns from an animation

Generated CSS uses turn units; the theme file needs a hex.

Input
hsl(0.5turn 80% 40%)
Output
#14b8b8

One lightness, six hues, and what each actually emits

Huehsl()HexLight emittedOKLCH lightness
Redhsl(0, 100%, 50%)#ff000021.3%62.8%
Yellowhsl(60, 100%, 50%)#ffff0092.8%96.8%
Greenhsl(120, 100%, 50%)#00ff0071.5%86.6%
Cyanhsl(180, 100%, 50%)#00ffff78.7%90.5%
Bluehsl(240, 100%, 50%)#0000ff7.2%45.2%
Magentahsl(300, 100%, 50%)#ff00ff28.5%70.2%

Every row claims 50% lightness. The emitted figures are WCAG relative luminance and span a factor of 12.85 between the yellow and the blue — the OKLCH column is the same set measured by a scale that matches the eye.

Where each of these three spaces is the right one

SpaceUse it forWatch out for
HSLNudging a colour by hand — a hover state, a border one step darkerIts lightness is not perceptual, so steps of equal L are not steps of equal brightness
HSB / HSVReading numbers off Figma, Photoshop or SketchCSS has no hsb() function; the digits must be converted, not pasted
OKLCHBuilding a palette where hues must match in brightnessNot every triple fits in sRGB — high chroma at extreme lightness gets clamped
HexStoring the answer: config files, SVG, design-tool import fieldsCarries no colour-space tag — the digits mean sRGB by convention
RGBArithmetic, canvas pixel data, terminal escapesThree independent channels with no hue axis to nudge along

The conversions between all five are exact in this direction; only the reverse from hex into whole-number HSL loses anything.

Habits that keep a colour scale honest

  • Judge a scale by the luminance figures, not by the L values — if the numbers under two swatches differ by 3x, the swatches differ by 3x no matter what the CSS says.
  • Author palettes in oklch() and let the build emit hex if you need it — the lightness axis is the one that survives a hue change.
  • When a colour copied from a design tool looks wrong, check the HSB row here before checking the handoff — the two spaces share only the hue.
  • Keep hex as the stored form in a design system. It is the only one of these with no rounding question attached to it.
  • A hue of 0 and a hue of 360 are the same colour; a saturation of 0 makes hue irrelevant entirely, which is why greys read as hsl(0, 0%, x%) whatever you typed.
  • Percent signs are optional here but not in CSS — hsl(217 91 60) is invalid in a stylesheet even though this page reads it.

Where HSL quietly moves the colour

The reverse direction is the lossy one

HSL to hex is exact. Hex to whole-number HSL is not: integer HSL has 3,672,360 combinations that land on only 2,037,745 distinct hex values, about 12.1% of sRGB. Google’s #1a73e8 has no integer HSL at all — the nearest rounding renders as #1c74e9. If a picker shows you HSL and you copy those numbers, you have changed the colour.

Equal lightness steps are not equal brightness steps

A scale from 10% to 90% L in even increments looks even in the numbers and bunched in the eye, because the relationship between L and emitted light is not linear. This is the same reason a 50% grey is not half as bright as white — it emits 21.6%.

HSB numbers pasted into hsl() give the wrong colour

They agree on hue and nothing else. At full saturation and full brightness in HSB you have the most vivid form of a hue; at full saturation and full lightness in HSL you have white. The failure is silent because the syntax is identical and the result is a valid colour.

Contrast ratios need luminance, not lightness

WCAG contrast is computed from relative luminance — the figure shown above — and two colours can sit at the same HSL lightness with wildly different ratios against the same background. Picking text colours by matching L values does not produce accessible pairs.

Alpha does not survive this conversion

An hsla() alpha is read and dropped here, because the hex this page emits is six digits. When a colour needs transparency, the RGB to HEX converter handles the eight-digit form and the rgba() spelling alongside it.

Parsing, precision, and what the readouts measure

Accepted input
hsl() and hsla(), written with commas or with the newer spaces; alpha after a slash or as a fourth argument, read and discarded; bare "h, s, l" and "h s l" triples; percent signs optional; hue in deg, rad, grad or turn
Hue and clamping
Hue wraps modulo 360, so 400 reads as 40 and -60 as 300; saturation and lightness clamp to 0–100, matching how CSS treats out-of-range values
Conversion
HSL to RGB by the CSS Color Level 4 formula, then rounded once to the 0–255 channels a hex can store — the rounding happens before anything is measured, so every figure describes the colour you actually get
Luminance
WCAG relative luminance: channels linearised through the sRGB transfer function, then weighted 0.2126 red, 0.7152 green, 0.0722 blue
OKLCH
Björn Ottosson’s OKLab converted to polar form, the same transform CSS Color Level 4 specifies for the oklch() function
Reverse coverage
The 3,672,360 integer HSL triples produce 2,037,745 distinct hex values, verified by exhaustive enumeration in the test suite
Scope
Alpha, CMYK and named colours are out of scope here; the RGB to HEX converter and HEX to RGB converter cover those on the shared engine
Network
None from tool code. A test sweep calls every function this page uses with fetch and XMLHttpRequest replaced by stubs that throw, so a stray request fails the build instead of shipping. Disconnect from the network and the page still works.

Questions about HSL, hex and lightness

How do I convert HSL to hex?

Convert HSL to RGB first, then write each channel as two hex digits. The middle step uses chroma = (1 − |2L − 1|) × S, which is the CSS Color Level 4 formula; the page above does it live and shows the result as you drag. The conversion is exact — no colour information is lost going this way.

Is hsl to hex conversion lossy?

Not in that direction. Every HSL value maps to exactly one hex code. The reverse is where loss happens: whole-number HSL can only express about 12.1% of the colours a hex holds, so a hex converted to rounded HSL and back does not always return to where it started.

Why do two colours at 50% lightness look so different?

Because HSL lightness is the midpoint between the strongest and weakest channel, not a measure of light. hsl(60, 100%, 50%) emits 92.8% of white and hsl(240, 100%, 50%) emits 7.2% — a factor of 12.85, despite the identical L. Use the luminance readout above to compare colours honestly.

What is the difference between HSL and HSB?

They share a hue axis and nothing else. HSB’s brightness maxes out at the most vivid form of a hue; HSL’s lightness maxes out at white. Design tools show HSB and CSS accepts HSL, so digits copied between them silently change the colour — hsb(210, 100%, 100%) is #0080ff and hsl(210, 100%, 100%) is #ffffff.

Should I use hsl() or oklch() in CSS?

Use oklch() when brightness must stay consistent across hues, which is most of what a palette is for. Use hsl() for a quick manual nudge to a single colour. Both are supported in every current browser; oklch() costs you the guarantee that the value fits in sRGB, since out-of-gamut triples get clamped.

Do I even need to convert, if browsers accept hsl()?

Often not — hsl() has been valid CSS for many years and needs no conversion to work. Converting matters when the value has to leave CSS: SVG attributes, design-tool import fields, terminal themes, and config files that only take hex. Inside a stylesheet, keeping the HSL is usually the more editable choice.

Is anything I paste here sent anywhere?

The work is JavaScript running in this tab. Every function it calls is covered by a test that stubs fetch and XMLHttpRequest to throw, so a request that slipped in would break the build rather than reach a server — and you can confirm it for yourself by disconnecting and carrying on.