Convert colors
instantly.
HEX, RGB, HSL, CMYK -- all formats, real-time conversion, one-click copy. The only free converter that remembers your recent colors.
Color conversion code: HEX, RGB, HSL, and CMYK in any language
Hextly converts HEX, RGB, HSL, and CMYK in the browser. This reference collects the conversion math itself as copy-paste code in the languages designers and developers most often need it, plus a syntax cheatsheet and the common pitfalls that bite round-trips.
JavaScript and TypeScript
function hexToRgb(hex) {
const h = hex.replace(/^#/, '');
const full = h.length === 3 ? h.split('').map(c => c + c).join('') : h;
return {
r: parseInt(full.slice(0, 2), 16),
g: parseInt(full.slice(2, 4), 16),
b: parseInt(full.slice(4, 6), 16),
};
}
function rgbToHsl(r, g, b) {
r /= 255; g /= 255; b /= 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
const l = (max + min) / 2;
if (max === min) return { h: 0, s: 0, l: Math.round(l * 100) };
const d = max - min;
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
let h;
if (max === r) h = (g - b) / d + (g < b ? 6 : 0);
else if (max === g) h = (b - r) / d + 2;
else h = (r - g) / d + 4;
return { h: Math.round(h * 60), s: Math.round(s * 100), l: Math.round(l * 100) };
}
function rgbToCmyk(r, g, b) {
const k = 1 - Math.max(r, g, b) / 255;
if (k === 1) return { c: 0, m: 0, y: 0, k: 100 };
return {
c: Math.round(((1 - r / 255 - k) / (1 - k)) * 100),
m: Math.round(((1 - g / 255 - k) / (1 - k)) * 100),
y: Math.round(((1 - b / 255 - k) / (1 - k)) * 100),
k: Math.round(k * 100),
};
}Python
import colorsys
def hex_to_rgb(hex_str: str) -> tuple[int, int, int]:
h = hex_str.lstrip('#')
if len(h) == 3:
h = ''.join(c * 2 for c in h)
return int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)
def rgb_to_hsl(r: int, g: int, b: int) -> tuple[int, int, int]:
h, l, s = colorsys.rgb_to_hls(r / 255, g / 255, b / 255)
return round(h * 360), round(s * 100), round(l * 100)
def rgb_to_cmyk(r: int, g: int, b: int) -> tuple[int, int, int, int]:
if r == g == b == 0:
return 0, 0, 0, 100
k = 1 - max(r, g, b) / 255
c = (1 - r / 255 - k) / (1 - k)
m = (1 - g / 255 - k) / (1 - k)
y = (1 - b / 255 - k) / (1 - k)
return round(c * 100), round(m * 100), round(y * 100), round(k * 100)colorsys swaps the order to HLS (hue, lightness, saturation), not HSL. Repack the tuple before exposing to UI code that expects HSL.
Go
package color
import (
"fmt"
"math"
"strings"
)
func HexToRGB(hex string) (r, g, b uint8) {
h := strings.TrimPrefix(hex, "#")
if len(h) == 3 {
h = string([]byte{h[0], h[0], h[1], h[1], h[2], h[2]})
}
fmt.Sscanf(h, "%02x%02x%02x", &r, &g, &b)
return
}
func RGBToHSL(r, g, b uint8) (h, s, l float64) {
rf, gf, bf := float64(r)/255, float64(g)/255, float64(b)/255
mx := math.Max(rf, math.Max(gf, bf))
mn := math.Min(rf, math.Min(gf, bf))
l = (mx + mn) / 2
if mx == mn {
return 0, 0, math.Round(l * 100)
}
d := mx - mn
if l > 0.5 {
s = d / (2 - mx - mn)
} else {
s = d / (mx + mn)
}
switch mx {
case rf:
h = (gf - bf) / d
if gf < bf {
h += 6
}
case gf:
h = (bf-rf)/d + 2
case bf:
h = (rf-gf)/d + 4
}
return math.Round(h * 60), math.Round(s * 100), math.Round(l * 100)
}Swift
import UIKit
extension UIColor {
static func from(hex: String) -> UIColor? {
var h = hex.trimmingCharacters(in: .whitespacesAndNewlines)
if h.hasPrefix("#") { h.removeFirst() }
if h.count == 3 { h = h.map { "\($0)\($0)" }.joined() }
guard h.count == 6, let value = UInt32(h, radix: 16) else { return nil }
let r = CGFloat((value >> 16) & 0xFF) / 255
let g = CGFloat((value >> 8) & 0xFF) / 255
let b = CGFloat(value & 0xFF) / 255
return UIColor(red: r, green: g, blue: b, alpha: 1)
}
var hex: String {
var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
getRed(&r, green: &g, blue: &b, alpha: &a)
return String(format: "#%02X%02X%02X",
Int(r * 255), Int(g * 255), Int(b * 255))
}
}PHP
function hexToRgb(string $hex): array {
$h = ltrim($hex, '#');
if (strlen($h) === 3) {
$h = $h[0].$h[0].$h[1].$h[1].$h[2].$h[2];
}
return [
'r' => hexdec(substr($h, 0, 2)),
'g' => hexdec(substr($h, 2, 2)),
'b' => hexdec(substr($h, 4, 2)),
];
}
function rgbToHex(int $r, int $g, int $b): string {
return sprintf('#%02x%02x%02x', $r, $g, $b);
}CSS preprocessors
CSS itself shipped rgb() with alpha via slash syntax in Level 4: rgb(123 97 255 / 0.5). The legacy comma form rgba(123, 97, 255, 0.5) still parses; new code can drop the a suffix and the commas.
SQL (storing color values)
-- Extract RGB channels from a HEX string (PostgreSQL)
SELECT
('x' || substring(hex from 2 for 2))::bit(8)::int AS r,
('x' || substring(hex from 4 for 2))::bit(8)::int AS g,
('x' || substring(hex from 6 for 2))::bit(8)::int AS b
FROM colors;Format syntax cheatsheet
CMYK math, worked
The math-only Adobe formula Hextly uses:
K = 1 - max(R, G, B) / 255
C = (1 - R/255 - K) / (1 - K)
M = (1 - G/255 - K) / (1 - K)
Y = (1 - B/255 - K) / (1 - K)Worked example for #7B61FF (RGB 123, 97, 255):
A saturated sRGB violet like #7B61FF lands outside the US Web Coated SWOP v2 gamut. A press would print a duller version of it. The formula above tells you nothing about that fall-off; an ICC profile does.
Conversion pitfalls
Related concepts
- sRGB
- IEC 61966-2-1 standardized the default monitor color space in 1996. Every HEX, RGB, and HSL value is implicitly sRGB unless tagged with color().
- Wide-gamut color
- CSS Color Level 4 added color(display-p3 ...) and lab(). Both exceed sRGB. Conversion path: P3 → sRGB clips, sRGB → P3 inflates.
- OKLCH
- A perceptually uniform color space gaining adoption in design systems (Tailwind 4 ships it). Hue and chroma behave more linearly than HSL across the gamut.
- ICC profile
- A file that maps device-independent color values to a specific output device (US Web Coated SWOP v2, GRACoL 2013, custom proof). Required for press-accurate CMYK.
- Total ink limit
- The maximum sum of C + M + Y + K a press can hold. Coated stock typically caps at 300%, uncoated at 240%. The naive CMYK formula ignores this.