How to Use a Color Picker: Pick, Sample, and Convert Colors
A color picker is a tool that lets you select or sample a color and get its exact code — in HEX, RGB, HSL, or another format. Whether you're matching a brand color from a logo, sampling a hue from a photograph, or building a UI palette, a color picker removes the guesswork.
This guide explains how color pickers work, how to use browser APIs to pick colors from the screen, how to sample pixels from images with Canvas, and how to convert between color formats in JavaScript, Python, Go, and PHP.
What Is a Color Picker?
A color picker is an interface — in a design tool, a browser DevTools panel, or a standalone web app — that lets you:
- Choose a color visually (saturation/lightness gradient + hue slider)
- Sample a color from the screen or an image (eyedropper)
- Convert a color between formats (HEX ↔ RGB ↔ HSL ↔ OKLCH)
The output is always a numeric color value you can paste directly into CSS, code, or a design file.
The Three Main Color Formats
| Format | Example | Best for |
|---|---|---|
| HEX | #3b82f6 |
CSS, HTML attributes, most design tools |
| RGB | rgb(59, 130, 246) |
When you need channel values (e.g., opacity math) |
| HSL | hsl(217, 91%, 60%) |
When you want to adjust hue, saturation, or lightness intuitively |
They all describe the same color. A color picker lets you see and copy any of them.
Picking a Color from the Screen — EyeDropper API
Modern browsers (Chrome 95+, Edge 95+, Opera 81+) ship the EyeDropper API, which lets a web page open a native screen sampler:
// Works in Chrome/Edge/Opera — check support first
async function pickFromScreen() {
if (!window.EyeDropper) {
alert('EyeDropper API not supported in this browser');
return null;
}
const eyeDropper = new EyeDropper();
try {
// Opens the OS-level colour sampler cursor
const result = await eyeDropper.open();
// result.sRGBHex → "#3b82f6"
return result.sRGBHex;
} catch (err) {
// User pressed Escape or cancelled
return null;
}
}
// Usage
document.getElementById('pick-btn').addEventListener('click', async () => {
const hex = await pickFromScreen();
if (hex) {
document.getElementById('preview').style.background = hex;
document.getElementById('output').textContent = hex;
}
});
Firefox note: Firefox does not support EyeDropper as of 2026. For cross-browser support, fall back to a canvas-based picker (see below).
Sampling a Color from an Image — Canvas API
To read the color of a pixel at position (x, y) in an image:
function sampleColorFromImage(imgElement, x, y) {
const canvas = document.createElement('canvas');
canvas.width = imgElement.naturalWidth;
canvas.height = imgElement.naturalHeight;
const ctx = canvas.getContext('2d');
// drawImage requires the image to be fully loaded
ctx.drawImage(imgElement, 0, 0);
// getImageData returns [R, G, B, A] for each pixel
const [r, g, b, a] = ctx.getImageData(x, y, 1, 1).data;
return {
hex: `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`,
rgb: `rgb(${r}, ${g}, ${b})`,
alpha: (a / 255).toFixed(2),
};
}
// Click handler on an <img> element
imgElement.addEventListener('click', (e) => {
const rect = imgElement.getBoundingClientRect();
// Scale click position to natural image dimensions
const x = Math.round((e.clientX - rect.left) * (imgElement.naturalWidth / rect.width));
const y = Math.round((e.clientY - rect.top) * (imgElement.naturalHeight / rect.height));
const color = sampleColorFromImage(imgElement, x, y);
console.log(color); // { hex: "#3b82f6", rgb: "rgb(59, 130, 246)", alpha: "1.00" }
});
CORS warning: If the image is on a different domain,
getImageDatathrows aSecurityErrorunless the server sendsAccess-Control-Allow-Origin: *and you setimgElement.crossOrigin = 'anonymous'before settingsrc.
Converting Between HEX, RGB, and HSL
Most color pickers need conversion functions. Here's a compact set:
// HEX → RGB
function hexToRgb(hex) {
const clean = hex.replace('#', '');
const full = clean.length === 3
? clean.split('').map(c => c + c).join('')
: clean;
return {
r: parseInt(full.slice(0, 2), 16),
g: parseInt(full.slice(2, 4), 16),
b: parseInt(full.slice(4, 6), 16),
};
}
// RGB → HEX
function rgbToHex(r, g, b) {
return '#' + [r, g, b]
.map(v => Math.round(v).toString(16).padStart(2, '0'))
.join('');
}
// RGB → HSL (H: 0–360, S/L: 0–100)
function rgbToHsl(r, g, b) {
r /= 255; g /= 255; b /= 255;
const max = Math.max(r, g, b), min = Math.min(r, g, b);
let h, s;
const l = (max + min) / 2;
if (max === min) {
h = s = 0;
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
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),
};
}
// HSL → RGB
function hslToRgb(h, s, l) {
s /= 100; l /= 100;
const k = n => (n + h / 30) % 12;
const a = s * Math.min(l, 1 - l);
const f = n => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
return {
r: Math.round(f(0) * 255),
g: Math.round(f(8) * 255),
b: Math.round(f(4) * 255),
};
}
// Example: full round-trip
const rgb = hexToRgb('#3b82f6'); // { r: 59, g: 130, b: 246 }
const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b); // { h: 217, s: 91, l: 60 }
const back = hslToRgb(hsl.h, hsl.s, hsl.l); // { r: 59, g: 130, b: 246 }
console.log(rgbToHex(back.r, back.g, back.b)); // "#3b82f6"
Python — Sample a Color from an Image
from PIL import Image
def sample_color(image_path: str, x: int, y: int) -> dict:
img = Image.open(image_path).convert("RGBA")
r, g, b, a = img.getpixel((x, y))
return {
"hex": f"#{r:02x}{g:02x}{b:02x}",
"rgb": f"rgb({r}, {g}, {b})",
"alpha": round(a / 255, 2),
}
# Get the dominant color (most frequent pixel after resizing to 1×1)
def dominant_color(image_path: str) -> str:
img = Image.open(image_path).convert("RGB")
# Resize to 1×1 — Pillow averages all pixels
tiny = img.resize((1, 1), Image.LANCZOS)
r, g, b = tiny.getpixel((0, 0))
return f"#{r:02x}{g:02x}{b:02x}"
print(sample_color("photo.jpg", 100, 200))
print(dominant_color("logo.png"))
Go — Sample a Pixel from an Image
package main
import (
"fmt"
"image"
_ "image/jpeg"
_ "image/png"
"os"
)
func sampleColor(path string, x, y int) (string, error) {
f, err := os.Open(path)
if err != nil {
return "", err
}
defer f.Close()
img, _, err := image.Decode(f)
if err != nil {
return "", err
}
c := img.At(x, y)
r, g, b, _ := c.RGBA()
// RGBA() returns 0–65535; shift right 8 to get 0–255
return fmt.Sprintf("#%02x%02x%02x", r>>8, g>>8, b>>8), nil
}
func main() {
hex, err := sampleColor("photo.jpg", 100, 200)
if err != nil {
panic(err)
}
fmt.Println(hex) // e.g. "#3b82f6"
}
PHP — Sample a Color from an Image
function sampleColor(string $path, int $x, int $y): array {
$img = imagecreatefromstring(file_get_contents($path));
if ($img === false) {
throw new RuntimeException("Cannot load image: $path");
}
$colorIndex = imagecolorat($img, $x, $y);
$rgb = imagecolorsforindex($img, $colorIndex);
imagedestroy($img);
$r = $rgb['red'];
$g = $rgb['green'];
$b = $rgb['blue'];
return [
'hex' => sprintf('#%02x%02x%02x', $r, $g, $b),
'rgb' => "rgb($r, $g, $b)",
];
}
// Dominant colour via GD resize to 1×1
function dominantColor(string $path): string {
$src = imagecreatefromstring(file_get_contents($path));
$tiny = imagecreatetruecolor(1, 1);
imagecopyresampled($tiny, $src, 0, 0, 0, 0, 1, 1, imagesx($src), imagesy($src));
$idx = imagecolorat($tiny, 0, 0);
$rgba = imagecolorsforindex($tiny, $idx);
imagedestroy($src);
imagedestroy($tiny);
return sprintf('#%02x%02x%02x', $rgba['red'], $rgba['green'], $rgba['blue']);
}
$color = sampleColor('photo.jpg', 100, 200);
echo $color['hex']; // #3b82f6
echo dominantColor('logo.png');
Quick Reference
| Task | Browser | Node.js | Python | Go | PHP |
|---|---|---|---|---|---|
| Pick from screen | EyeDropper API |
— | — | — | — |
| Sample pixel from image | Canvas getImageData |
sharp + metadata |
Pillow getpixel |
image.At(x,y) |
imagecolorat |
| Dominant color | Canvas resize 1×1 | sharp.resize(1,1) |
img.resize((1,1)) |
manual loop | imagecopyresampled 1×1 |
| HEX → RGB | hexToRgb() above |
same | PIL.ImageColor |
manual parse | imagecolorat + imagecolorsforindex |
| RGB → HSL | rgbToHsl() above |
same | colorsys.rgb_to_hls |
manual math | manual math |
6 Common Mistakes
1. Forgetting CORS when sampling images from another domain
getImageData throws SecurityError on cross-origin images. Always set img.crossOrigin = 'anonymous' before setting src, and the server must send CORS headers.
2. Not accounting for device pixel ratio
On Retina displays, getBoundingClientRect() gives CSS pixels but getImageData works in physical pixels. Multiply click coordinates by window.devicePixelRatio when the canvas is rendered at full resolution.
3. Treating EyeDropper as universally supported
Firefox and Safari do not support the EyeDropper API. Always check window.EyeDropper before calling it and provide a canvas-based fallback.
4. Ignoring the alpha channel
getImageData returns [R, G, B, A]. If the image has transparency (PNG with alpha), the A value affects the apparent color. For a 6-digit hex output, you're discarding alpha — document this if precision matters.
5. Using Math.random() to generate color swatches for design
Math.random() gives colors spread across the full gamut, most of which look muddy or washed out. For readable swatches, generate in HSL with constrained saturation (55–70%) and lightness (45–60%).
6. Rounding errors in HEX ↔ RGB round-trips
parseInt('3b', 16) === 59 but (59).toString(16) === '3b' — fine. The problem is HSL conversion: intermediate floating-point steps accumulate errors. Always clamp each channel to [0, 255] and use Math.round before converting back to hex.
Frequently Asked Questions
What is the EyeDropper API? It's a browser API (available in Chromium-based browsers) that opens the OS-native colour sampler cursor and returns the hex code of whatever pixel the user clicks on — anywhere on the screen, including outside the browser window.
Can I pick a color from a video frame?
Yes. Draw the current video frame onto a <canvas> with ctx.drawImage(videoElement, 0, 0), then use getImageData at the click position. You must handle CORS the same way as with images if the video is cross-origin.
What's the difference between HSL and HSV? HSL (Hue–Saturation–Lightness): white is L=100%, black is L=0%, pure hue is S=100%, L=50%. HSV (Hue–Saturation–Value): pure hue is S=100%, V=100%; black is V=0%. Most web color pickers use HSL or HSB (HSB = HSV). CSS uses HSL.
How do I get the dominant color from an image?
The simplest way is to resize the image to 1×1 pixel and read that pixel — the resize filter averages all colors. For a palette of 5–10 dominant colors, use k-means clustering (libraries: node-vibrant for JS, colorthief for Python/JS).
Why does my color look different on screen vs in print?
Screens use additive RGB (light); printers use subtractive CMYK (ink). A vivid RGB blue like #0000ff can't be reproduced exactly in CMYK. For print-accurate work, always proof in a CMYK-aware tool and convert with ICC profiles.
Can I use a color picker to check contrast ratios? Yes. Once you have two hex colors, compute the WCAG relative luminance of each and divide to get the contrast ratio. WCAG AA requires ≥4.5:1 for normal text, ≥3:1 for large text. The color-converter tool can do this for you.