Color Mixer — Blend Two Colors into a Swatch Scale
Color Mixer · Tools · Plain HTML, CSS & JS · Live preview
Copy & paste
Source Code
<div class="cm-card">
<div class="cm-ends">
<label class="cm-end"><span>From</span>
<div class="cm-swatch"><input type="color" id="cmA" value="#6366f1"><input type="text" id="cmAHex" value="#6366F1" maxlength="7" spellcheck="false"></div>
</label>
<label class="cm-end"><span>To</span>
<div class="cm-swatch"><input type="color" id="cmB" value="#22d3ee"><input type="text" id="cmBHex" value="#22D3EE" maxlength="7" spellcheck="false"></div>
</label>
</div>
<label class="cm-steps">Steps <strong id="cmStepsVal">7</strong>
<input type="range" id="cmSteps" min="3" max="11" step="1" value="7">
</label>
<div class="cm-scale" id="cmScale"></div>
<code class="cm-css" id="cmCss"></code>
</div>*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#0f172a;color:#e2e8f0;display:flex;justify-content:center;padding:36px 18px}
.cm-card{background:#1e293b;border:1px solid #334155;border-radius:18px;padding:18px;width:100%;max-width:400px}
.cm-ends{display:flex;gap:12px;margin-bottom:14px}
.cm-end{flex:1;display:flex;flex-direction:column;gap:6px;font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.05em;color:#94a3b8}
.cm-swatch{display:flex;align-items:center;gap:8px;background:#0f172a;border:1px solid #334155;border-radius:10px;padding:6px 8px}
.cm-swatch input[type=color]{width:28px;height:28px;border:none;background:none;padding:0;cursor:pointer}
.cm-swatch input[type=text]{border:none;outline:none;background:none;font-size:13px;font-family:ui-monospace,monospace;width:100%;text-transform:uppercase;color:#e2e8f0}
.cm-steps{display:flex;align-items:center;gap:10px;font-size:12px;font-weight:700;color:#94a3b8;margin-bottom:14px}
.cm-steps strong{color:#e2e8f0;min-width:18px}
.cm-steps input{flex:1;accent-color:#6366f1}
.cm-scale{display:flex;border-radius:10px;overflow:hidden;height:64px;border:1px solid #334155}
.cm-cell{flex:1;position:relative;cursor:pointer;transition:flex .12s}
.cm-cell:hover{flex:1.4}
.cm-cell span{position:absolute;left:0;right:0;bottom:4px;text-align:center;font-size:9px;font-weight:700;font-family:ui-monospace,monospace;opacity:0;transition:opacity .12s}
.cm-cell:hover span{opacity:1}
.cm-copied{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:800;color:#fff;background:rgba(0,0,0,.45)}
.cm-css{display:block;margin-top:12px;background:#0f172a;border:1px solid #334155;border-radius:8px;padding:9px 12px;font-family:ui-monospace,monospace;font-size:11px;color:#7dd3fc;cursor:pointer;word-break:break-all;line-height:1.5}function toRgb(hex) { return [parseInt(hex.slice(1,3),16), parseInt(hex.slice(3,5),16), parseInt(hex.slice(5,7),16)]; }
function toHex(rgb) { return '#' + rgb.map(function (c) { return Math.round(c).toString(16).padStart(2,'0'); }).join('').toUpperCase(); }
function clampHex(v) { v = v.trim(); if (v[0] !== '#') v = '#' + v; if (/^#[0-9a-fA-F]{3}$/.test(v)) v = '#' + v[1]+v[1]+v[2]+v[2]+v[3]+v[3]; return /^#[0-9a-fA-F]{6}$/.test(v) ? v : null; }
function lum(rgb) { return 0.2126*rgb[0] + 0.7152*rgb[1] + 0.0722*rgb[2]; }
var a = document.getElementById('cmA'), aHex = document.getElementById('cmAHex');
var b = document.getElementById('cmB'), bHex = document.getElementById('cmBHex');
var steps = document.getElementById('cmSteps'), stepsVal = document.getElementById('cmStepsVal');
var scale = document.getElementById('cmScale'), cssEl = document.getElementById('cmCss');
function mix(c1, c2, t) { return [c1[0]+(c2[0]-c1[0])*t, c1[1]+(c2[1]-c1[1])*t, c1[2]+(c2[2]-c1[2])*t]; }
function render() {
var ca = toRgb(a.value), cb = toRgb(b.value);
var n = parseInt(steps.value, 10);
stepsVal.textContent = n;
scale.innerHTML = '';
var hexes = [];
for (var i = 0; i < n; i++) {
var t = n === 1 ? 0 : i / (n - 1);
var rgb = mix(ca, cb, t);
var hex = toHex(rgb);
hexes.push(hex);
var cell = document.createElement('div');
cell.className = 'cm-cell';
cell.style.background = hex;
var label = document.createElement('span');
label.textContent = hex;
label.style.color = lum(rgb) > 140 ? '#0f172a' : '#fff';
cell.appendChild(label);
cell.addEventListener('click', function (hx, cl) {
return function () {
navigator.clipboard && navigator.clipboard.writeText(hx);
var tag = document.createElement('div'); tag.className = 'cm-copied'; tag.textContent = 'Copied'; cl.appendChild(tag);
setTimeout(function () { tag.remove(); }, 700);
};
}(hex, cell));
scale.appendChild(cell);
}
cssEl.textContent = 'linear-gradient(90deg, ' + hexes.join(', ') + ')';
}
[a, b].forEach(function (inp) { inp.addEventListener('input', function () { (inp === a ? aHex : bHex).value = inp.value.toUpperCase(); render(); }); });
[aHex, bHex].forEach(function (inp) { inp.addEventListener('input', function () { var c = clampHex(inp.value); if (c) { (inp === aHex ? a : b).value = c; render(); } }); });
steps.addEventListener('input', render);
cssEl.addEventListener('click', function () { navigator.clipboard && navigator.clipboard.writeText(cssEl.textContent); });
render();Extend with AI
Take This Snippet Further With AI
The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.
What's included
Features
About this UI Snippet
Color Mixer — Interpolate a Swatch Scale Between Two Colors

A color mixer blends two colors into an evenly-spaced scale of swatches — the quick way to build a gradient, generate tints between a brand pair, or pick a mid-point shade. This snippet interpolates the colors in the browser, lets you set how many steps to generate, and copies any swatch or the full gradient, in plain HTML, CSS, and vanilla JavaScript.
Linear interpolation in RGB
The mixer converts each end color from hex to RGB, then for each step computes a position t from 0 to 1 and linearly interpolates every channel: c1 + (c2 - c1) * t. With n steps the positions are spread i / (n - 1) so the first swatch is exactly the "from" color, the last is exactly the "to" color, and the rest are even blends. The result is rounded back to a hex string per swatch — straightforward, predictable color math with no library.
Adjustable resolution
A range slider sets the number of steps from 3 to 11, re-rendering the scale live. Three steps give you a from / mid / to triad; eleven give a smooth ramp you can pull individual tints from. This is handy for generating a tint scale for a design system (50→500→900) or for finding the precise middle color between two brand hues.
Readable labels, click to copy
Each swatch shows its hex on hover, and the label color flips between dark and light based on the swatch's luminance so it stays legible on both pale and dark blends. Clicking a swatch copies its hex to the clipboard with a brief "Copied" confirmation — so the mixer doubles as a palette picker, not just a preview.
Dual input, kept in sync
Both end colors have a native <input type="color"> swatch and a hex field that stay in sync both ways: pick from the OS color picker and the hex updates, or type a hex (3- or 6-digit, with or without #) and the swatch follows. A small validator normalizes and ignores invalid input so the scale never breaks mid-typing.
Gradient output
Below the scale, the mixer prints a ready-to-use linear-gradient(90deg, …) built from the generated stops, and clicking it copies the whole declaration. That turns a two-color choice into a paste-ready CSS gradient or a discrete palette in one step. The pure interpolation helpers are easy to lift into a theme generator or design tool, making this a compact, dependency-free reference for blending colors.
Step by step
How to Use
- 1Paste HTML, CSS, and JSA mixer renders with two end colors and a generated swatch scale.
- 2Choose two colorsUse the swatch pickers or type hex values for the From and To colors.
- 3Set the stepsDrag the slider to generate 3–11 evenly-spaced blends.
- 4Copy a swatchHover to see its hex; click to copy it to the clipboard.
- 5Grab the gradientClick the linear-gradient() string to copy a paste-ready CSS gradient.
- 6Reuse the mathLift the mix/toHex helpers into a palette or theme generator.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
Each end color is converted to RGB, then for every step the mixer computes a position t from 0 to 1 and linearly interpolates each channel as c1 + (c2 - c1) * t, rounding back to hex. Positions are spread as i / (n - 1), so the first and last swatches exactly match your two colors and the middle ones are even blends.
Each label's color is chosen from the swatch's luminance (a weighted sum of its RGB channels): light swatches get a dark label and dark swatches get a light one, so the hex text stays legible across the whole scale. It is the same readability idea behind contrast-aware text on colored backgrounds.
This version interpolates in sRGB, which is simple and predictable and fine for most UI gradients and tint scales. If you need perceptually even steps you can swap the mix function to interpolate in HSL, OKLCH, or LAB — the rest of the snippet (steps, copy, gradient output) stays the same because it only depends on getting a color per step.
Yes. The mixer builds a linear-gradient(90deg, …) from the generated stops and shows it below the scale; click it to copy the full declaration. You can also click any individual swatch to copy its hex if you want discrete palette values rather than a gradient.
Keep the toRgb, toHex, and mix helpers as plain functions and hold the two colors and step count in state. Compute the swatch array as derived state and render it, with click handlers that copy via the Clipboard API. Two-way bind the color and hex inputs to the same state. Tailwind users swap the classes for utilities; the interpolation logic is unchanged.