The problem: you typed #3B82F6 into a design tool and got three different HSL values
You have a brand color #3B82F6 (Tailwind blue-500). You paste it into Figma and read hsl(217, 91%, 60%). You paste the same hex into Photoshop and read H:217 S:88 B:96. You paste it into a print shop's portal and read C:76 M:62 Y:0 K:4. Three tools, three numbers, same color. The first is HSL (Hue, Saturation, Lightness). The second is HSV (Hue, Saturation, Value, sometimes called HSB). The third is CMYK. They disagree because they describe the same RGB color through three different geometric models — HSL is a cylinder, HSV is a different cylinder, CMYK is a subtractive ink model. The honest move is a converter that parses the input once into RGB and renders all 10 formats from that single source of truth, instead of chaining conversions that drift.
Fastest path
Open the Color Converter, type #3B82F6, read the 10 output formats.
Input: #3B82F6
→ HEX: #3b82f6
→ RGB: rgb(59, 130, 246)
→ HSL: hsl(217, 91%, 60%)
→ HSV: hsv(217, 76%, 96%)
→ CMYK: cmyk(76%, 47%, 0%, 4%)
→ LAB: lab(54 20 -58)
→ LCH: lch(54 61 289)
→ CSS: cornflowerblue (nearest named)
→ Contrast vs #ffffff: 3.57:1 → fails AA normal
→ Tints: 5 lighter · Shades: 5 darker · Tones: 5 desaturated
The tool parsed the hex into {r:59, g:130, b:246}, then ran eight conversion functions against that one RGB object and rendered all formats in parallel. The rest of this guide is why HSL is a cylinder while RGB is a cube, why HSV and HSL disagree on saturation at 100%, why LAB exists, why CMYK is subtractive, and why the WCAG contrast formula has a 0.05 in it.
The substance: one source of truth, ten formats, three geometries
RGB as the source of truth
The tool's parseColorInput accepts HEX, RGB, HSL, and CSS named colors. Whatever you type, it returns one RGB object {r, g, b} — three integers 0–255. Every other format is computed from that RGB object by a dedicated function: rgbToHsl, rgbToHsv, rgbToCmyk, rgbToLab, rgbToLch. There is no HSL-to-HSV function, no CMYK-to-LAB function. Conversions always go RGB → target, never source → target. This is the same pattern as the byte converter's "everything goes through bytes" — one base unit, many renderings.
RGB itself is a cube. Each axis (R, G, B) runs 0–255, and every color is a point inside that 256×256×256 cube — 16,777,216 points. The black corner is (0,0,0); the white corner is (255,255,255). The three primaries (red, green, blue) sit on their own axes at full brightness. This is why monitors emit RGB — each pixel is three light emitters (R, G, B subpixels), and the color you see is additive: more light = lighter color, all lights at full = white.
HSL is a cylinder, HSV is a different cylinder
HSL rewrites the RGB cube as a cylinder. Hue (0–360°) is the angle around the central axis. Saturation (0–100%) is the distance from the axis. Lightness (0–100%) is the height — 0 is black at the bottom, 100 is white at the top, 50 is pure color at the equator. The rgbToHsl function does this: it normalizes R, G, B to 0–1, finds max and min, computes L = (max + min) / 2, then derives S from (max - min) divided by either (2 - max - min) (when L > 0.5) or (max + min) (when L ≤ 0.5). The hue comes from which of R, G, B is the max — red gives (g-b)/d, green gives (b-r)/d + 2, blue gives (r-g)/d + 4.
HSV is also a cylinder, but the vertical axis is Value (a.k.a. Brightness), not Lightness. The difference: in HSL, 100% Lightness is white regardless of saturation. In HSV, 100% Value is the brightest the hue gets — white only happens when saturation is 0. This is why #3B82F6 reads as HSL 91% saturation, 60% lightness but HSV 76% saturation, 96% value. Same color. HSL says "this is a medium-bright, very saturated blue." HSV says "this is an almost-maximum brightness blue, with 24% whiteness mixed in." Neither is wrong; they answer different questions. HSL answers "how close to black or white?" HSV answers "how close to the brightest version of this hue?"
The rgbToHsv function shows the split: s = max === 0 ? 0 : d / max where d = max - min. Saturation is the gap between the brightest channel and the dimmest, divided by the brightest. Value is just max itself. That's it. HSV is one normalization away from RGB; HSL is two.
CMYK is subtractive — and that's why K exists
CMYK (Cyan, Magenta, Yellow, Key/black) is the print model. It's subtractive — each ink absorbs (subtracts) a range of light. Cyan absorbs red, magenta absorbs green, yellow absorbs blue. Overlapping all three should absorb everything and produce black. In practice, the result is a muddy brown because inks aren't spectrally pure. So printers add a separate black ink (K) for two reasons: true black is cheaper than stacking three inks, and text looks sharper with a dedicated black plate.
The rgbToCmyk function computes K = 1 - max(r, g, b) / 255 first — K is how far the brightest channel is from full. If K is 1 (input is pure black), it short-circuits to {0, 0, 0, 100}. Otherwise, each of C, M, Y is (1 - channel - K) / (1 - K) — the remaining ink mix after accounting for the black plate. The K-first ordering is why the conversion is correct: you figure out the black ink share first, then express the color cast as the remaining three inks. Convert the other way and you'd stack four inks when three would do.
LAB and LCH: perceptual color spaces
LAB (CIE Lab*) exists because RGB, HSL, and HSV are device models — they describe what the screen does, not what the eye sees. Two colors with the same RGB value on two different monitors look different. Two colors with the same LAB value look the same to a standard human observer, regardless of device. LAB is the color space used for color management, soft-proofing, and "match this swatch across print and screen" workflows.
The conversion goes RGB → XYZ → LAB. The rgbToXyz function first linearizes each channel (v ≤ 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055)^2.4) — this undoes the sRGB gamma encoding. Then it multiplies by a 3×3 matrix with the sRGB-to-XYZ primaries (the constants 0.4124564, 0.3575761, etc.). The xyzToLab function applies a nonlinear cube-root transform with a threshold (t > 0.008856 ? cbrt(t) : 7.787t + 16/116) and scales against the D65 white point (x/0.95047, y/1, z/1.08883). L is lightness (0–100), a is green↔red, b is blue↔yellow. The labToLch function is just polar coordinates: c = sqrt(a² + b²) (chroma), h = atan2(b, a) * 180/π (hue). LCH is LAB with a polar hue instead of opposing a/b axes — easier for designers because "rotate the hue 30°" is one operation in LCH and two in LAB.
The WCAG contrast ratio and the 0.05
The Contrast tab computes the WCAG contrast ratio between the current color and a second color. The contrastRatio function divides the two relative luminances: (lighter + 0.05) / (darker + 0.05). The 0.05 is not a fudge factor — it accounts for ambient light reflected off the screen. A perfectly black screen in a lit room still shows some light, so the darkest real-world luminance isn't 0, it's about 0.05. Without that offset, black-on-black would compute as infinite contrast instead of the real-world 1:1.
The relativeLuminance function uses the same linearization as rgbToXyz but with the WCAG-specific constants (v ≤ 0.03928 ? v/12.92 : ((v+0.055)/1.055)^2.4) and weights: 0.2126*R + 0.7152*G + 0.0722*B. The weights are the Rec. 709 luminance coefficients — green dominates because the human eye is most sensitive to green. The tool checks four WCAG thresholds: AA Large (3:1), AA Normal (4.5:1), AAA Large (4.5:1), AAA Normal (7:1). #3B82F6 against white scores 3.57:1 — passes AA Large, fails AA Normal. Blue-500 is not body-text-safe on white; it's a background or a large-heading color.
Color harmonies: the color wheel as geometry
The Harmonies tab generates five classic color schemes from the base hue: complementary (180°), triadic (120° and 240°), split-complementary (150° and 210°), tetradic/square (90°, 180°, 270°), analogous (±15° and ±30°). Each function takes the base HSL, adds a fixed angle to the hue modulo 360, and keeps saturation and lightness unchanged. This is the color wheel as literal geometry — hues are angles, and harmonies are rotations. The tool renders each harmony as clickable swatches; click one to load it as the base color.
Tints, shades, and tones are the three ways to desaturate a color. Tints add white (the generateTints function raises L toward 100 in equal steps). Shades add black (lowers L toward 0). Tones reduce saturation (the generateTones function lowers S in equal steps, leaving L alone). Five of each, evenly spaced. The distinction matters: a tint of red becomes pink; a shade of red becomes maroon; a tone of red becomes dusty rose. Same hue, three different jobs.
The CSS named color lookup
The tool ships 140 CSS named colors (aliceblue through yellowgreen) in CSS_NAMED_COLORS. When you enter a hex, it checks for an exact match first. If no exact match, it finds the nearest named color by Euclidean distance in RGB space — sqrt((r-nr)² + (g-ng)² + (b-nb)²) against every named color. #3B82F6 has no exact named-color match, but it's closest to cornflowerblue (#6495ED). The nearest-name lookup is why a designer can type cornflowerblue and get a color, or type a hex and get the nearest name back — useful for writing CSS that humans can read.
Gotchas
- HSL and HSV disagree on saturation.
#3B82F6is 91% saturated in HSL but 76% in HSV. Same color, different definitions of "saturation." HSL saturation is the gap between max and min divided by a lightness-aware denominator; HSV saturation is the gap divided by max. Don't mix them — if your design tool says "S:88" and your converter says "S:91," check which one is HSL and which is HSV before you "correct" anything. - HEX is sRGB, not linear RGB. The hex
#3B82F6encodes gamma-corrected sRGB values. The luminance and LAB conversions linearize first (v ≤ 0.04045 ? v/12.92 : ...). If you skip the linearization, contrast ratios and LAB values come out wrong — a common bug in hand-rolled converters. - CMYK conversion is approximate. The tool computes CMYK from sRGB values using a naive formula. Real print color management uses ICC profiles that account for the specific paper, ink set, and press. Use the tool's CMYK for ballpark estimates; use a profiled workflow for production print.
- The WCAG 0.05 is not optional. The contrast formula is
(L_lighter + 0.05) / (L_darker + 0.05), notL_lighter / L_darker. Without the offset, black-on-white computes as infinite contrast. The 0.05 models ambient screen reflection — it's in the spec for a reason. - LAB is perceptual, not intuitive. L is lightness (0–100), but a and b are opposing axes (green↔red, blue↔yellow), not hue and saturation. If you want "rotate this color's hue 30°," use LCH — it's LAB in polar form. The tool's
labToLchdoes the conversion withatan2andsqrt. - Alpha is not part of the conversion. The tool's alpha slider produces HEX8 (8-digit hex with alpha) and RGBA/HSLA strings, but alpha is orthogonal — it doesn't change the underlying RGB color, only how it composites over a background. Two colors with the same RGB and different alpha are the same color, displayed differently.
- Named colors are nearest-neighbor, not exact. The tool returns the closest named color when no exact match exists.
#3B82F6is calledcornflowerbluebut the actual cornflowerblue hex is#6495ED— visibly different. The label is approximate; trust the hex. - Harmonies rotate hue only. Complementary, triadic, analogous — all keep saturation and lightness constant and rotate hue. This is why a 90%-saturation 60%-lightness base produces a 90%-saturation 60%-lightness complement. If your base is very dark, the complement is also very dark; adjust lightness manually after.
- 3-digit hex expands by doubling, not padding.
#3B5becomes#33BB55, not#00003Bor#3B5000. The tool'shexToRgbhandles 3-digit by duplicating each char. This is the CSS spec — don't try to "fix" it.
Summary
- RGB is the source of truth. The tool parses every input (HEX, RGB, HSL, named color) into one
{r, g, b}object and renders all 10 formats from that. No chained conversions, no drift. RGB is a cube — three axes 0–255, every color is a point in 16.7M space. - HSL and HSV are cylinders with different vertical axes. HSL's axis is Lightness (100% = white). HSV's axis is Value (100% = brightest hue). Same color reads as 91% saturation in HSL and 76% in HSV because the denominators differ. Know which one your tool reports before comparing.
- CMYK is subtractive and K-first. Inks absorb light, so more ink = darker. The tool computes K first (
1 - max(r,g,b)/255), then expresses the remaining cast as C, M, Y. Pure black short-circuits to{0,0,0,100}. Real print needs ICC profiles — the tool gives a ballpark. - LAB and LCH are perceptual. LAB linearizes sRGB, converts to XYZ with the sRGB primaries, applies a cube-root transform against the D65 white point. LCH is LAB in polar form —
c = sqrt(a²+b²),h = atan2(b,a). Use LCH when you want to "rotate hue"; use LAB when you want device-independent color matching. - WCAG contrast has a 0.05 offset. The formula is
(L_lighter + 0.05) / (L_darker + 0.05), modeling ambient screen reflection. The tool checks four thresholds: AA Large (3:1), AA Normal (4.5:1), AAA Large (4.5:1), AAA Normal (7:1).#3B82F6on white scores 3.57:1 — fails AA for body text. - Harmonies are hue rotations. Complementary (+180°), triadic (+120°/+240°), split-complementary (+150°/+210°), tetradic (+90°/+180°/+270°), analogous (±15°/±30°). Tints add white, shades add black, tones reduce saturation — five each, evenly spaced.
- Convert at the Color Converter; for favicons that need exact brand colors use Favicon Generator, for embedding color values in HTML/CSS use Image to Base64, and for QR code colors use QR Code Generator.