Why Random Palettes Are Useful
Most designers default to the same colors. Look at any portfolio site, any SaaS landing page, any 2026 dashboard — it is overwhelmingly blue, with a sprinkle of purple. Random palette generation is a tool for breaking out of that rut. You spin the wheel, see five combinations you would never have picked, and recognize one that fits the brief.
The other use is rapid prototyping. When wireframing a multi-tenant app where each customer gets a brand color, you need to verify that the layout works across the full hue spectrum. Hand-picking 50 brand colors is tedious. Generating 50 random palettes that all pass contrast checks takes seconds.
The naive approach — pick three random hex values — produces ugly results 95% of the time. Color theory exists to make randomness look intentional. The trick is constraining the random space.
The HSL Color Model
RGB is how monitors render color. HSL (Hue, Saturation, Lightness) is how humans think about it. For random generation, HSL wins decisively because each axis is independently meaningful.
Hue is a 0-360° angle on the color wheel. 0 is red, 120 green, 240 blue. Saturation is 0-100%, where 0 is grayscale and 100 is fully vivid. Lightness is 0-100%, where 0 is black, 50 is the pure hue, and 100 is white.
The reason HSL beats RGB for generation: in RGB, changing one channel changes both hue and brightness in unpredictable ways. rgb(255,0,0) and rgb(0,255,0) have wildly different perceived brightness even though both have one channel maxed. In HSL, fixing lightness at 55 and rotating hue produces colors that all feel about the same brightness. That cohesion is what makes a palette look designed instead of random.
Pro tip: Pick saturation in the 55-75% range and lightness in the 45-60% range. Outside these bands the palette starts to look either washed out or fluorescent. Vary hue, fix the rest.
Color Theory Cheatsheet
Six classical schemes cover 99% of palette generation. Each is defined by a set of hue offsets from a seed hue:
| Scheme | Hue offsets | Use case |
|---|---|---|
| Analogous | 0°, +30°, -30° | Soft backgrounds, low-stress UI, illustrations |
| Complementary | 0°, +180° | CTA against background, status badges |
| Split-complementary | 0°, +150°, +210° | Vivid but less harsh than complementary |
| Triadic | 0°, +120°, +240° | Playful brand systems, charts, dashboards |
| Tetradic | 0°, +90°, +180°, +270° | Multi-category data visualization |
| Monochromatic | 0° (vary L only) | SaaS UIs, minimal brand systems |
Analogous Colors
Analogous schemes pick three hues within a ±30° wedge of the color wheel. The result is calm, harmonious, low-contrast — like a sunset photo, the colors blend into each other. Sky-blue with teal and cyan. Burnt orange with red and amber.
Analogous palettes are the safest random pick. They almost never clash because the hues are physically close. The downside is exactly that lack of contrast: an analogous palette gives you nowhere to put a CTA button that needs to grab attention. Use analogous for backgrounds, illustrations, and secondary surfaces; reach for a complementary accent when you need emphasis.
Complementary Colors
Complementary pairs sit directly opposite on the wheel — 180° apart. Blue and orange. Red and green. Yellow and purple. The maximum perceptual contrast between two hues. This is why every safety vest is yellow-orange against blue sky, why the IKEA logo is yellow-on-blue, why "buy now" buttons are usually orange on a blue site.
The danger is that pure complementary colors at full saturation vibrate uncomfortably. The solution is to use complementary as a 90/10 split: 90% of the layout in one hue (varied by lightness), with the complement reserved for the one element that must stand out. This is the entire logic of the "primary + accent" pattern in modern design systems.
Triadic Colors
Triadic palettes pick three hues evenly spaced 120° apart. The classic example is red-yellow-blue (the primary triad). Triadic has a unique property: every pair within the palette has the same perceptual contrast. Nothing dominates by hue alone; balance comes from how you assign size, weight, and position.
This makes triadic the workhorse of dashboards and data visualization. Three categories of bars in a chart, three statuses (success, warning, error), three brand color tokens — triadic gives you a balanced set without one color visually shouting over the others. Mute the saturation slightly (60% vs 80%) to prevent the colors from competing too hard.
Monochromatic
Monochromatic schemes pick one hue and vary only saturation and lightness. The result feels disciplined and corporate, which is why almost every B2B SaaS tool uses it. Stripe, Linear, Notion, Figma — open them side by side and you will see the same trick: a single brand hue rendered in 8-12 lightness steps from near-black to near-white.
The advantage is that monochromatic palettes are the easiest to make accessible. You can verify contrast once across the lightness scale and reuse the same scale for every screen. The disadvantage is monotony — and the fact that every SaaS now looks the same. If you want to stand out, monochromatic is the wrong default.
WCAG Contrast Checks
No palette ships without an accessibility check. WCAG 2.1 specifies contrast ratios as the minimum acceptable luminance difference between text and its background:
AA — Required
- 4.5:1 for normal body text
- 3:1 for large text (18pt+ or 14pt bold)
- 3:1 for UI components and graphical objects
AAA — Aspirational
- 7:1 for normal body text
- 4.5:1 for large text
- Required for some government and healthcare sites
The ratio is computed from relative luminance: L = 0.2126·R + 0.7152·G + 0.0722·B (with RGB linearized first). The ratio between two colors is (L1 + 0.05) / (L2 + 0.05) with the larger luminance on top. Any random-palette generator that does not run this check ships inaccessible colors regularly.
Generating Palettes in Code
Here is a minimal triadic generator with HSL-to-RGB conversion. Drop it into any prototype to get repeatable, harmonious random palettes:
// Generate a triadic palette in HSL, output as hex
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, 9 - k(n), 1));
return [Math.round(255 * f(0)), Math.round(255 * f(8)), Math.round(255 * f(4))];
}
function rgbToHex(r, g, b) {
return '#' + [r, g, b].map(v => v.toString(16).padStart(2, '0')).join('');
}
function triadicPalette(seedHue = Math.floor(Math.random() * 360)) {
const hues = [seedHue, (seedHue + 120) % 360, (seedHue + 240) % 360];
return hues.map(h => {
const [r, g, b] = hslToRgb(h, 65, 55); // fixed S/L for cohesion
return { hue: h, hex: rgbToHex(r, g, b), rgb: [r, g, b] };
});
}
console.log(triadicPalette(200));
// [
// { hue: 200, hex: '#3a93cf', rgb: [58, 147, 207] },
// { hue: 320, hex: '#cf3a93', rgb: [207, 58, 147] },
// { hue: 80, hex: '#93cf3a', rgb: [147, 207, 58] }
// ]Two design choices in this snippet matter. First, saturation and lightness are fixed (65% and 55%) — this is what gives the three colors visual cohesion despite being 120° apart. Second, the seed hue is the only random input. Reseed with triadicPalette(120) for green-anchored, triadicPalette(0) for red-anchored, and so on. To get a full design system, generate a 9-step lightness scale per hue (lightness 5, 15, 25... 95) and you have 27 tokens that all relate.
For production use, wrap this in a WCAG check: reject any palette where text colors fail 4.5:1 against the lightest background. Most random-generator failures come from skipping that one validation.
Generate accessible palettes
Random color schemes with built-in WCAG contrast checks — copy as hex, RGB, or HSL.