How to Convert Hex to RGB (and Back): Complete Color Code Guide
Web colors live in two worlds: the six-character hex codes that designers paste into CSS (#3a86ff) and the RGB triplets that programmers use in canvas drawing, image processing, and color math (rgb(58, 134, 255)). Converting between them is a 30-second job once you know the formula — and a five-minute job if you don't.
This guide covers both directions (hex → RGB and RGB → hex), explains why the formats exist, gives ready-to-run code in four languages, and answers the questions that trip people up most often.
What Is a Hex Color Code?
A hex color is three bytes — one each for red, green, and blue — written in base-16 (hexadecimal):
#RRGGBB
Each pair of hex digits goes from 00 (decimal 0) to FF (decimal 255). So #ff0000 is pure red (R=255, G=0, B=0) and #000000 is black.
| Format | Example | Range |
|---|---|---|
| Hex | #3a86ff |
00–FF per channel |
| RGB | rgb(58, 134, 255) |
0–255 per channel |
| RGB % | rgb(22.7%, 52.5%, 100%) |
0%–100% |
Browsers understand both formats in CSS. JavaScript Canvas, most image libraries, and color-math functions typically want RGB integers — so conversion is necessary.
The Hex → RGB Formula
Split the six-character code into three two-character chunks and convert each from base 16 to base 10:
R = parseInt("RR", 16)
G = parseInt("GG", 16)
B = parseInt("BB", 16)
Example — #3a86ff:
| Chunk | Hex | Decimal |
|---|---|---|
| RR | 3a |
58 |
| GG | 86 |
134 |
| BB | ff |
255 |
Result: rgb(58, 134, 255)
The RGB → Hex Formula
Convert each channel to a two-digit hex string, zero-pad if needed, and concatenate:
hex = "#" + toHex(R) + toHex(G) + toHex(B)
toHex(n) = n.toString(16).padStart(2, "0")
Example — rgb(58, 134, 255):
| Channel | Decimal | Hex | Padded |
|---|---|---|---|
| R | 58 | 3a |
3a |
| G | 134 | 86 |
86 |
| B | 255 | ff |
ff |
Result: #3a86ff
Code Examples
JavaScript
// Hex → RGB
function hexToRgb(hex) {
const clean = hex.replace(/^#/, "");
const r = parseInt(clean.slice(0, 2), 16);
const g = parseInt(clean.slice(2, 4), 16);
const b = parseInt(clean.slice(4, 6), 16);
return { r, g, b };
}
// RGB → Hex
function rgbToHex(r, g, b) {
return (
"#" +
[r, g, b]
.map((v) => v.toString(16).padStart(2, "0"))
.join("")
);
}
console.log(hexToRgb("#3a86ff")); // { r: 58, g: 134, b: 255 }
console.log(rgbToHex(58, 134, 255)); // "#3a86ff"
Python
def hex_to_rgb(hex_color: str) -> tuple[int, int, int]:
h = hex_color.lstrip("#")
return tuple(int(h[i:i+2], 16) for i in (0, 2, 4))
def rgb_to_hex(r: int, g: int, b: int) -> str:
return f"#{r:02x}{g:02x}{b:02x}"
print(hex_to_rgb("#3a86ff")) # (58, 134, 255)
print(rgb_to_hex(58, 134, 255)) # "#3a86ff"
Go
package main
import (
"fmt"
"strconv"
)
func hexToRGB(hex string) (r, g, b uint8) {
if len(hex) > 0 && hex[0] == '#' {
hex = hex[1:]
}
val, _ := strconv.ParseUint(hex, 16, 32)
r = uint8(val >> 16)
g = uint8((val >> 8) & 0xFF)
b = uint8(val & 0xFF)
return
}
func rgbToHex(r, g, b uint8) string {
return fmt.Sprintf("#%02x%02x%02x", r, g, b)
}
func main() {
r, g, b := hexToRGB("#3a86ff")
fmt.Println(r, g, b) // 58 134 255
fmt.Println(rgbToHex(58, 134, 255)) // #3a86ff
}
PHP
function hexToRgb(string $hex): array {
$hex = ltrim($hex, '#');
return [
'r' => hexdec(substr($hex, 0, 2)),
'g' => hexdec(substr($hex, 2, 2)),
'b' => hexdec(substr($hex, 4, 2)),
];
}
function rgbToHex(int $r, int $g, int $b): string {
return sprintf('#%02x%02x%02x', $r, $g, $b);
}
print_r(hexToRgb('#3a86ff')); // r=58, g=134, b=255
echo rgbToHex(58, 134, 255); // #3a86ff
Handling the Short (3-digit) Hex Format
CSS allows a 3-digit shorthand: #rgb is equivalent to #rrggbb — each digit is repeated.
#3af → #33aaff
#f00 → #ff0000
Expand before parsing:
function expandHex(hex) {
const clean = hex.replace(/^#/, "");
if (clean.length === 3) {
return "#" + clean.split("").map((c) => c + c).join("");
}
return "#" + clean;
}
Hex with Alpha (8-digit / RGBA)
CSS also supports #RRGGBBAA — an 8-digit hex with an alpha channel:
#3a86ffcc → rgba(58, 134, 255, 0.8)
The alpha byte cc (204 in decimal) divided by 255 gives the CSS alpha: 204 / 255 ≈ 0.8.
function hexToRgba(hex) {
const clean = hex.replace(/^#/, "");
const r = parseInt(clean.slice(0, 2), 16);
const g = parseInt(clean.slice(2, 4), 16);
const b = parseInt(clean.slice(4, 6), 16);
const a = clean.length === 8
? Math.round((parseInt(clean.slice(6, 8), 16) / 255) * 100) / 100
: 1;
return { r, g, b, a };
}
Bonus: Converting to HSL
RGB is not great for color math (lightening, darkening, rotating hue). HSL (Hue / Saturation / Lightness) is more intuitive:
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;
switch (max) {
case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break;
case g: h = ((b - r) / d + 2) / 6; break;
case b: h = ((r - g) / d + 4) / 6; break;
}
return { h: Math.round(h * 360), s: Math.round(s * 100), l: Math.round(l * 100) };
}
console.log(rgbToHsl(58, 134, 255)); // { h: 217, s: 100, l: 61 }
To lighten #3a86ff by 10%, convert to HSL, add 10 to L, convert back to hex.
Quick Reference Table
| Hex | R | G | B | Color |
|---|---|---|---|---|
#ff0000 |
255 | 0 | 0 | Red |
#00ff00 |
0 | 255 | 0 | Green |
#0000ff |
0 | 0 | 255 | Blue |
#ffff00 |
255 | 255 | 0 | Yellow |
#ff00ff |
255 | 0 | 255 | Magenta |
#00ffff |
0 | 255 | 255 | Cyan |
#ffffff |
255 | 255 | 255 | White |
#000000 |
0 | 0 | 0 | Black |
#808080 |
128 | 128 | 128 | Gray |
FAQ
Why do hex colors use base 16? Because two hex digits can represent exactly 256 values (0–255) — the range of one color channel. Base 10 would need three digits for the same range and would be harder to compact to short codes.
Are hex colors case-sensitive?
No. #3A86FF and #3a86ff are identical. CSS accepts both; convention is lowercase.
What does parseInt("ff", 16) return?
255. The second argument 16 tells parseInt to interpret the string as base 16.
My color looks wrong after converting — why?
Check for the 3-digit short form (#3af) — expand it before parsing. Also check for an extra # or a missing leading zero (e.g., #030201 not #32 1).
Can I convert hex to HSL directly? Not directly — go hex → RGB → HSL. The intermediate RGB step is necessary.
What's the difference between rgba() and the 8-digit hex?
They represent the same thing. rgba(58, 134, 255, 0.8) and #3a86ffcc are equivalent. Browser support for 8-digit hex is solid from 2018 onwards (Chrome 62, Firefox 49, Safari 10).
How do I pick the right color format for CSS?
Use hex for static colors in stylesheets (compact, designer-friendly). Use rgb() / rgba() when you need to manipulate channels dynamically in JavaScript. Use hsl() when you need to generate color palettes or calculate lighter/darker shades programmatically.