HSL to HWB converter
Describe a color as a hue plus white and black: hsl(210, 80%, 45%) becomes whiteness 9% and blackness 19% on the same hue, written as hwb(210 9% 19%). Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Worked example
- H
- 210
- S
- 80
- L
- 45
- W
- 9
- B
- 19
- Css
- hwb(210 9% 19%)
HSL describes a color by hue on the wheel, saturation as how vivid the mix is, and lightness as how close the mix sits to black or white. HWB uses that same hue, then names how much white and how much black are mixed into the pure hue. Designers who already pick colors in HSL often need HWB when they write modern CSS Color Module hwb() functions, talk to illustration tools that expose whiteness and blackness sliders, or document a swatch as a tint plus a shade. This converter takes hue in degrees from zero through three hundred sixty and saturation and lightness as percentages from zero through one hundred, then returns the matching HWB triple with a CSS hwb() string.
How HSL maps to hue, whiteness, and blackness
Hue is shared by HSL and HWB: zero degrees is pure red, one hundred twenty is pure green, two hundred forty is pure blue, and three hundred sixty closes the circle back at red. HSL then stores saturation as how far a gray of the chosen lightness is stretched toward the fully chromatic color at that hue, and lightness as the mix between black at zero percent, a mid-tone near fifty percent, and white at one hundred percent.
HWB instead names the same color as a pure hue mixed with a white amount and a black amount. On the unit interval the mapping is closed-form and does not need a trip through integer RGB: chroma equals one minus the absolute value of two times lightness minus one, all multiplied by saturation; whiteness is lightness minus half of chroma; blackness is one minus lightness minus half of chroma.
That is the same as taking the minimum RGB channel as white and one minus the maximum RGB channel as black, which is how the CSS Color Module Level Four specification defines HWB. When saturation is zero the chroma vanishes, so whiteness and blackness become complementary percentages that sum to one hundred and describe a gray, black, or white. When lightness is fifty percent and saturation is one hundred percent, chroma is one and both white and black collapse to zero, which is the pure hue.
Hue must lie in the closed interval from zero to three hundred sixty; saturation and lightness must each lie between zero and one hundred percent inclusive.
What you send and what you get back
Send three finite numbers under the fields h, s, and l. The field h is hue in degrees and is required; it must sit between zero and three hundred sixty inclusive because HSL and HWB share that angle and this converter will not invent a wrap. The fields s and l are saturation and lightness as percentages from zero to one hundred.
When saturation sits outside zero to one hundred the response explains that s must be a percentage in that closed band so a client can clamp or fix the token before retrying; the same rule applies to lightness.
Classic checks include mid-tone blue-cyan at hue two hundred ten, saturation eighty, lightness forty-five mapping to nine percent white and nineteen percent black; pure red as hue zero, saturation one hundred, lightness fifty mapping to zero white and zero black; and any hue with zero saturation collapsing to an achromatic mix whose white plus black equals one hundred.
Where HSL to HWB conversion shows up in real work
Design systems often author brand ramps in HSL so product teams can step lightness for accessible contrast while keeping hue and a saturation band stable, yet modern stylesheets, illustration apps, and several native pickers now expose HWB because mixing a pure hue with white and black matches how painters talk about tints and shades. Front-end engineers who migrate a token from hsl() custom properties to hwb() need a deterministic oracle that does not depend on a particular browser's serialization of computed style.
Data visualization and generative art tools that build categorical palettes by stepping hue at fixed saturation and lightness still have to dump HWB when a renderer or a print pipeline asks for whiteness and blackness rather than a third lightness axis. Curriculum and bootcamp lessons teach cylindrical models side by side and need a single trusted key for homework that matches the CSS Color Module identities rather than a screenshot of a color picker.
Accessibility workflows that adjust perceived lightness in HSL and then hand off to an HWB-based editor still need exact channel mapping without browser-only APIs. If you later need HSL to RGB, HSL to HSV, multi-format HEX LAB CMYK conversion, or WCAG contrast checks, treat those as separate search intents and landings rather than overloading this dedicated HSL to HWB converter page.
What you can do with it
Design tokens from HSL to CSS hwb()
Convert HSL brand ramps into hue, whiteness, and blackness triples for modern stylesheets, illustration pickers, and documentation tables that speak HWB.
Tint and shade documentation
Express an HSL swatch as a pure hue mixed with white and black so design reviews can talk about tints and shades without a second color model.
FAQ
What ranges do hue, saturation, and lightness accept?
Hue h must be between 0 and 360 degrees inclusive. Saturation s and lightness l are percentages from 0 to 100.
Does hue change between HSL and HWB?
No. Hue is shared by both models and is copied unchanged. Only whiteness and blackness are computed from saturation and lightness.
How are whiteness and blackness computed?
On the unit interval, chroma C equals (1 − |2L − 1|) · S. Whiteness is L − C/2 and blackness is 1 − L − C/2. Results are returned as percentages from 0 to 100.
What does a CSS hwb() string look like?
The css field is a Color Module string such as hwb(210 9% 19%). Hue is the shared angle; the two percentages are whiteness and blackness.
What does the HSL to HWB converter cost?
The browser tool is free and runs locally.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/color/hsl-hwb \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"h":210,"s":80,"l":45}'const res = await fetch("https://api.kit.forhosting.com/color/hsl-hwb", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"h": 210,
"s": 80,
"l": 45
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/color/hsl-hwb",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"h": 210,
"s": 80,
"l": 45
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/color/hsl-hwb", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"h":210,"s":80,"l":45}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"h":210,"s":80,"l":45}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/color/hsl-hwb", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"h": 210,
"s": 80,
"l": 45
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "color.hsl_hwb",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
Technical notes
Saturation and lightness outside the legal ranges fail with a clear invalid_input error so bad tokens never silently clamp.
How HSL maps to hue, whiteness, and blackness
The pipeline is pure floating-point arithmetic with no random seeds, no wall-clock timestamps, and no network calls, so identical HSL inputs always produce identical JSON. Values outside those bands fail with invalid_input instead of silent wrap-around or clamp-without-notice.
What you send and what you get back
Missing fields, empty strings, non-numeric tokens, NaN, and Infinity fail with invalid_input naming the field and are not charged. On success the response echoes the cleaned h, s, and l values, reports whiteness w and blackness b as percentages, nests both the source hsl object and the result hwb object, emits a printable CSS string of the form hwb(H W% B%), and includes a short formula note that restates the chroma identities. Pricing for successful API calls is the published base of $0.002 per item; validation failures do not consume prepaid balance.
Where HSL to HWB conversion shows up in real work
Because this capability is a pure parse engine, API pricing is the published base of $0.002 per successful item and failed validation does not consume balance.
Integration use cases
Bulk token and curriculum fixtures
Script deterministic HSL to HWB conversion for regression fixtures, homework keys, and palette exports with one API at $0.002 per item.