Free Color Converter

Convert colors
instantly.

HEX, RGB, HSL, CMYK -- all formats, real-time conversion, one-click copy. The only free converter that remembers your recent colors.

Recent Colors
Start converting to build history
HEX
RGB
R
G
B
HSL
H
S
L
CMYK
C
M
Y
K
CMYK values are approximate -- use color-managed workflows for press-ready output.

Real-Time Conversion

Type in any format -- HEX, RGB, HSL, or CMYK -- and watch all others update instantly. No submit buttons, no waiting.

One-Click Copy

Every format has its own copy button. Click, paste into your code or design tool. Done in under two seconds.

Session History

Your last 10 colors persist across page refreshes via localStorage. The only free converter that remembers where you left off.

Reference

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

DirectionOne-liner
HEX → RGBh.match(/\w\w/g).map(x => parseInt(x, 16))
RGB → HEX'#' + [r,g,b].map(x => x.toString(16).padStart(2, '0')).join('')
RGB → CMYK key1 - Math.max(r, g, b) / 255
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

DirectionOne-liner
HEX → RGBtuple(int(h[i:i+2], 16) for i in (0, 2, 4))
RGB → HEX'#%02x%02x%02x' % (r, g, b)
RGB → HSLcolorsys.rgb_to_hls(r/255, g/255, b/255) (note: HLS order, not HSL)
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

DirectionOne-liner
HEX → RGBfmt.Sscanf(strings.TrimPrefix(h, "#"), "%02x%02x%02x", &r, &g, &b)
RGB → HEXfmt.Sprintf("#%02x%02x%02x", r, g, b)
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

OperationSassLess
Declare from hex$c: #7B61FF;@c: #7B61FF;
RGB channelsred($c), green($c), blue($c)red(@c), green(@c), blue(@c)
HSL channelshue($c), saturation($c), lightness($c)hue(@c), saturation(@c), lightness(@c)
Rotate hueadjust-hue($c, 30deg)spin(@c, 30)
Lightenlighten($c, 10%)lighten(@c, 10%)
Add alphargba($c, 0.5)fade(@c, 50%)

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)

Storage shapePatternUse when
CHAR(7)'#7B61FF'The most common shape; compact, sortable as text
INT UNSIGNED0x7B61FF (8085503)Compact; lets you bitmask channels; harder to scan by eye
Three TINYINT UNSIGNEDr, g, bChannel queries (WHERE r > 200 AND b < 100)
JSON column'{"h":255,"s":100,"l":69}'When the canonical form is HSL or you need alpha plus format flag together
-- 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

Input shapeAccepted by HextlySpec source
#7B61FFyesCSS Color Level 3 §4.2.1
7B61FFyes (auto-prepends #)not in spec; Hextly tolerance
#7BFyes (expands to #77BBFF)CSS Color Level 3 §4.2.1
rgb(123, 97, 255)yesCSS Color Level 3
rgb(123 97 255)yesCSS Color Level 4 §5.1
rgb(48%, 38%, 100%)yesCSS Color Level 4 §5.1
rgba(123, 97, 255, 0.5)yes (alpha noted; conversions show opaque)CSS Color Level 3
hsl(255, 100%, 69%)yesCSS Color Level 4 §5.2
hsl(255 100% 69%)yesCSS Color Level 4 §5.2
255 100% 69%yes (Tailwind --var shape)not in spec; Tailwind convention
cmyk(52, 62, 0, 0)yes (percent or 0-1 decimal both accepted)not a CSS notation; ISO 12647-2 for the model

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):

StepValue
max(R, G, B) / 255255 / 255 = 1.000
K1 - 1.000 = 0.000
C(1 - 0.482 - 0) / (1 - 0) = 0.518
M(1 - 0.380 - 0) / (1 - 0) = 0.620
Y(1 - 1.000 - 0) / (1 - 0) = 0.000
Rounded CMYK52, 62, 0, 0

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

SymptomCauseFix
rgb(123, 97, 255) round-trips to rgb(122, 97, 254)HSL stored as integer percentages; conversion back hits float precision and rounds awayStore HSL as floats internally, round only at display
Achromatic colors flicker through hue 0The algorithm pins H to 0 when R = G = B; any tweak jumps from thereHold the previous hue when saturation is 0, or hide the hue control
#000000 returns CMYK 0, 0, 0, 100 cleanly but the JS one-liner divides by zeroThe denominator 1 - K collapses when max(R, G, B) is 0Guard the divide-by-zero (the snippets above all check k === 1)
CSS named color names don't resolveHextly is a format converter; names come from the X11/SVG dictionaryLook up name → hex first (CSS Color Level 4 §6.1.1 lists all 147)
Printed color looks washed out vs screensRGB violet outside the CMYK gamut; the naive formula ignores ICC profile, paper, and dot gainRun a real separation in Photoshop or Illustrator with an output intent attached
#7B6 expands wrong in a hand-rolled parserShorthand doubles each digit, not the pair#7B6 expands to #77BB66, not #7B66B6

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.
Read more on /learn