Building a CSS Gradient Generator in the Browser — Linear, Radial, and Conic
Most CSS gradient generators do the basics: pick two colors, get a linear-gradient. I wanted to build one that handles all three gradient types, real-time preview, Tailwind output, and a pattern overlay system — all without touching a server.
Here's what I learned.
The Three Gradient Types
CSS supports three gradient functions, and they behave very differently under the hood.
Linear gradients are the simplest. You define an angle and color stops:
background: linear-gradient(135deg, #6366f1 0%, #ec4899 100%);
Radial gradients spread from a center point outward:
background: radial-gradient(circle, #0ea5e9 0%, #10b981 100%);
Conic gradients are the most unusual — they rotate around a center point like a color wheel:
background: conic-gradient(from 0deg, #ef4444, #eab308, #22c55e);
The Canvas Preview Problem
The tricky part wasn't generating the CSS string — it was rendering an accurate live preview using Canvas.
Canvas doesn't understand CSS gradients directly. You have to use createLinearGradient(), createRadialGradient(), or... figure out conic yourself, because createConicGradient() has limited browser support.
For conic gradients, I ended up rendering them as a series of thin pie slices:
function drawConic(ctx, cx, cy, radius, stops) {
const total = Math.PI * 2;
stops.forEach((stop, i) => {
const startAngle = stop.position * total;
const endAngle = (stops[i + 1]?.position ?? 1) * total;
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.arc(cx, cy, radius, startAngle, endAngle);
ctx.fillStyle = stop.color;
ctx.fill();
});
}
Not perfect for smooth gradients, but good enough for a preview at typical screen sizes.
Converting Angle to Canvas Coordinates
CSS uses clockwise degrees from the top (0deg = up). Canvas uses counterclockwise radians from the right. The conversion is non-obvious:
function cssAngleToCanvas(deg, width, height) {
const rad = ((deg - 90) * Math.PI) / 180;
const x1 = width / 2 - Math.cos(rad) * width / 2;
const y1 = height / 2 - Math.sin(rad) * height / 2;
const x2 = width / 2 + Math.cos(rad) * width / 2;
const y2 = height / 2 + Math.sin(rad) * height / 2;
return { x1, y1, x2, y2 };
}
Generating Tailwind Output
Tailwind uses directional utilities (to-r, to-br) rather than angles. I map the nearest 45-degree increment:
const dirMap = {
0: 'to-t', 45: 'to-tr', 90: 'to-r', 135: 'to-br',
180: 'to-b', 225: 'to-bl', 270: 'to-l', 315: 'to-tl'
};
const nearest = Math.round(angle / 45) * 45;
const dir = dirMap[nearest] ?? 'to-br';
For arbitrary colors, Tailwind needs bracket notation: from-[#6366f1].
SVG Pattern Overlay
I added five overlay patterns (dots, grid, diagonal, wave, triangles) rendered as repeating Canvas patterns. The trick is building a small offscreen canvas tile and using createPattern():
function buildDotPattern(ctx, color) {
const tile = document.createElement('canvas');
tile.width = 20; tile.height = 20;
const tc = tile.getContext('2d');
tc.fillStyle = color + '44';
tc.beginPath();
tc.arc(10, 10, 2, 0, Math.PI * 2);
tc.fill();
return ctx.createPattern(tile, 'repeat');
}
What I'd Do Differently
- Add interpolation mode (sRGB vs okLAB) — colors mix very differently
- Support mesh gradients (currently only possible via SVG filters)
- Let users drag color stops on a visual bar instead of sliders
The tool is live at toolzip.app/tools/gradient-generator — no install, no upload, runs entirely in your browser.
Top comments (0)