🎨 Color Palette Generator
Pull a palette from a photo or build one from a color. Every swatch carries its WCAG contrast ratio, and the image is read in your browser, never uploaded.
Opposite sides of the wheel. Maximum contrast — strong for a call to action against a dominant color, tiring if used in equal amounts.
Take it with you
#2563eb, #86a4e6, #ebad25, #e6c886, #0d3792
:root {
--color-1: #2563eb;
--color-2: #86a4e6;
--color-3: #ebad25;
--color-4: #e6c886;
--color-5: #0d3792;
}// tailwind.config.js
theme: {
extend: {
colors: {
'color-1': '#2563eb',
'color-2': '#86a4e6',
'color-3': '#ebad25',
'color-4': '#e6c886',
'color-5': '#0d3792',
},
},
}[
{
"name": "color-1",
"hex": "#2563eb",
"rgb": "rgb(37, 99, 235)",
"hsl": "hsl(221, 83%, 53%)",
"contrastWithWhite": 5.17,
"contrastWithBlack": 4.06
},
{
"name": "color-2",
"hex": "#86a4e6",
"rgb": "rgb(134, 164, 230)",
"hsl": "hsl(221, 66%, 71%)",
"contrastWithWhite": 2.48,
"contrastWithBlack": 8.47
},
{
"name": "color-3",
"hex": "#ebad25",
"rgb": "rgb(235, 173, 37)",
"hsl": "hsl(41, 83%, 53%)",
"contrastWithWhite": 1.99,
"contrastWithBlack": 10.54
},
{
"name": "color-4",
"hex": "#e6c886",
"rgb": "rgb(230, 200, 134)",
"hsl": "hsl(41, 66%, 71%)",
"contrastWithWhite": 1.62,
"contrastWithBlack": 12.97
},
{
"name": "color-5",
"hex": "#0d3792",
"rgb": "rgb(13, 55, 146)",
"hsl": "hsl(221, 84%, 31%)",
"contrastWithWhite": 10.61,
"contrastWithBlack": 1.98
}
]Contrast is measured against black and white only. Text in one of your own palette colors on another needs checking as a pair — and a color that fails here will fail everywhere it carries text, which is much cheaper to discover now than during an accessibility audit.
What Color Palette Generator Does
Color harmony is geometry on a wheel. Complementary colors sit opposite each other, triadic ones at the corners of an equilateral triangle, analogous ones side by side — the schemes are rotations of hue, which is why this generator works in HSL: it is the space where those relationships are actually defined. Convert to any other space and the classical relationships stop being simple rotations.
The schemes are a starting point rather than an answer. A complementary pair used in equal amounts is exhausting to look at; the same pair with one color dominant and the other reserved for a call to action is the most reliable interface palette there is. That is a proportion decision, and no generator makes it for you.
What this adds, and what the popular palette tools leave out, is the number that decides whether a palette survives contact with real content: contrast. Every swatch carries its ratio against black and white and says which to use on it. A palette that looks beautiful and fails WCAG is one you will discover has to be rebuilt, usually late, usually after it is already in production.
Contrast is computed the way the guideline defines it — relative luminance in linear light, not a simple brightness average — so the figures match what an accessibility checker will tell you rather than approximating it.
It will also pull a palette out of an image, which is the thing most people actually want from a palette tool and the part where implementations quietly differ. The clustering runs in linear light rather than on the stored bytes, because the byte 128 is not half the light of 255, it is 21.6% of it. Average the bytes across a black-and-white boundary and you get 128; average the light and you get 188. Gamma-space extraction pulls every color it finds toward the dark end, and on a photo with hard edges you can see it.
Nothing about any of this is a model, which matters if you have been burned by a generator that returns something different every time you ask. The harmonies are rotations of hue and the image colors are k-means over pixels — arithmetic with a defined answer, so the same input gives you the same palette today and next year. Your image is read in the browser and never uploaded.
How to Use Color Palette Generator
- Start from a color, or switch to "From an image" and choose a photo
- Pick a harmony, or set how many colors to pull out of the picture
- Check the contrast ratio printed on each swatch before committing to it
- Copy a single value, or export the palette as CSS variables, Tailwind config or JSON
Formula Used by Color Palette Generator
Contrast ratio
(L₁ + 0.05) ÷ (L₂ + 0.05), L = 0.2126·R + 0.7152·G + 0.0722·B in linear light
- L₁, L₂
- the relative luminance of the lighter and darker color
- linear light
- sRGB values are gamma-encoded and must be linearized first, or the answer is wrong
- the weights
- green dominates perceived brightness, which is why green text on white is harder to read than it looks
Worked example
#2563eb, a common interface blue, with white text on it.
- Linearize each channel, then L = 0.2126·R + 0.7152·G + 0.0722·B
- L for #2563eb ≈ 0.1387, L for white = 1.0
- (1.0 + 0.05) ÷ (0.1387 + 0.05)
Result: 5.17:1 — clears 4.5:1, so white body text on that blue passes.
The harmonies, and what they are for
| Scheme | Hue relationship | Character |
|---|---|---|
| Complementary | Opposite, 180° apart | Maximum contrast. Strong for one accent against a dominant color; tiring in equal amounts |
| Analogous | Neighbors, ±30–60° | Calm and cohesive. Needs an accent from elsewhere or it reads flat |
| Triadic | Three points 120° apart | Vivid and balanced; best with one lead and two accents |
| Split complementary | The complement, split ±30° | Most of the tension of complementary with much less clash |
| Tetradic | Two complementary pairs | Richest and hardest to balance — mute all but one |
| Monochromatic | One hue, varying lightness | Always harmonious; hierarchy comes from contrast, not hue |
WCAG contrast thresholds
What the ratio on each swatch has to clear.
| Content | Minimum (AA) | Enhanced (AAA) |
|---|---|---|
| Body text | 4.5:1 | 7:1 |
| Large text (18pt, or 14pt bold) | 3:1 | 4.5:1 |
| Interface components and graphics | 3:1 | — |
Source: WCAG 2.2 — Contrast (Minimum)
Why the stored number is not the brightness
The sRGB transfer curve, which is the reason both the contrast figures and the image extraction work in linear light. A gray written as 128 — exactly halfway through the byte range — emits barely a fifth of the light of white. Anything that averages the stored bytes is averaging a number that is not proportional to what you see.
| sRGB value | Share of white's light |
|---|---|
| 0 | 0.0% |
| 32 | 1.4% |
| 64 | 5.1% |
| 128 | 21.6% |
| 160 | 35.2% |
| 188 | 50.3% |
| 224 | 74.5% |
| 255 | 100.0% |
Source: WCAG — relative luminance
How to Read Your Result
Pick a dominant color, not five equal ones
The reason generated palettes often look worse in use than on the swatch grid is proportion. A common working rule is roughly 60% of a surface in a dominant neutral or light color, 30% in a secondary, and 10% in the accent. Five colors at equal weight reads as noise whatever the harmony.
A mid-tone that fails both is the trap
Colors around the middle of the lightness range frequently fail against black and white alike — the swatch shows a ratio near 3:1 either way. Those are fine as large blocks or borders and unusable behind body text. Finding that out here is cheaper than finding it out in an audit.
Contrast against black and white is the start, not the end
If you intend to put one palette color on another — a colored button on a colored section — that pair needs checking on its own. The ratios here tell you about text on each color, which covers most uses and not all of them.
Why a palette pulled from a photo comes out dark elsewhere
sRGB stores color with a transfer curve, so the numbers in the file are not proportional to light. Byte 128 emits 21.6% of the light of byte 255, not 50%. A palette extractor that averages those bytes — which is the obvious implementation, and a common one — is averaging the wrong quantity, and the error runs one way: toward the dark end. Over a boundary between black and white the byte average is 128 while the color with the true mean light output is 188, a gap of 60 levels out of 255. This one converts every pixel to linear light, clusters there, and converts the cluster centers back at the end. Verified on synthetic images whose answer is known by construction: three flat colors in 60/30/10 proportion come back as exactly those three colors with exactly those shares.
Where the colors come from, and what the percentage means
The image is scaled down so its longest edge is 160 pixels — the dominant colors of a thumbnail are the dominant colors of the original, and putting twelve million pixels through k-means buys nothing — then clustered into as many groups as you asked for. The percentage on each swatch is the share of opaque pixels that fell into that cluster, which is why the order is by prevalence rather than by prettiness. Fully transparent pixels are skipped rather than counted as black, which matters for logos on a transparent background: count them and every palette comes back with a black that is not in the picture.
Limitations & Accuracy Notes
- Contrast is reported against pure black and pure white only. Pairs of your own colors need checking individually.
- No Pantone, RAL, Sherwin-Williams or other proprietary color system. Those catalogs are licensed, and their values are not public data to map to — a tool claiming to give you "the Pantone" for a hex code is giving you a guess with a trademark attached. Take a physical swatch to the supplier instead.
- A palette extracted from an image describes that image. It is not a brand palette, and the dominant color of a photograph is frequently a background you do not want. Treat the result as a starting set to edit down.
- The extraction is deterministic, which means it has no taste. It returns the colors that cover the most pixels, so a picture that is 70% sky returns a lot of sky.
- The harmonies are geometric constructions in HSL. They are a reliable starting point, not a design; proportion, context and content do most of the work.
- HSL lightness is not perceptual — two colors at the same L can look very different in brightness, which is exactly why the contrast figure is shown rather than inferred from the lightness value.
- No color-blindness simulation. Palettes that distinguish states by hue alone can be unusable for some viewers regardless of contrast.
- Output is sRGB. Wide-gamut colors in Display P3 or oklch are not generated.
Frequently Asked Questions
What are color harmonies?
Can I get a palette from a photo?
Why do palettes extracted from photos often look too dark?
What color formats can I copy?
Can it give me Pantone or Sherwin-Williams codes?
Does this use AI?
What do the harmony types mean?
How many colors should a palette have?
Does a generated palette meet accessibility requirements?
Why do my colors look different on another screen?
Should I work in HSL rather than hex?
Is anything sent to a server?
References & Further Reading
- WCAG 2.2 — Contrast (Minimum) — The 4.5:1 and 3:1 thresholds and the contrast formula used on each swatch
- WCAG — relative luminance — The linearization step and the channel weights
- MDN — <hsl()> — The color space the harmonies are constructed in, and its limitations