CSS Gradient Generator

Build a gradient and blend it in a space that does not go grey in the middle

The gradient

Click the bar to add a stop, drag one to move it, and drag it off the bottom to remove it.

Shape and colour path

degrees

Ready made

The CSS

Drag the stops, pick a shape, and take the CSS. The blend space is the setting worth understanding, and there is a note under the controls telling you whether it makes any difference to the colours you have picked.

The grey middle

Halfway between two colours, measured:

red     #dc2626 to green #16a34a
  sRGB    #796438   a dark olive
  OKLab   #a17939   a brighter ochre

black   #000000 to white #ffffff
  sRGB    #808080
  OKLab   #636363

The second pair is the clearer lesson. Halfway
between black and white by the numbers is not
halfway between them by eye.

Everybody meets this eventually. A gradient between two saturated colours from opposite sides of the wheel goes muddy and dark in the middle, and it looks like a mistake. It is not a bug in your browser or in the colours you chose.

CSS has always interpolated in sRGB, one channel at a time. Red to green crosses through a point where both channels are about half, and half of each is a dark olive, not the bright yellow you might expect between them.

It is worth being straight about the size of the effect. For two shades of the same blue there is essentially none. For red to green it is obvious. For blue to yellow both spaces pass through something muted, because those two hues really are opposite and there is no bright colour between them to find; OKLab keeps a little more of it and holds a steadier lightness on the way.

Why another space fixes it

sRGB is a space for storing colours, not for
walking between them.

Doubling the number in a channel does not double
the brightness you see, so an even walk through
the numbers is an uneven walk through the colours.

OKLab was built so that equal steps look equal.
That is the whole difference.

OKLab is a colour space designed so that the distance between two points matches how different they look. Walking in a straight line through it produces a gradient that looks even, keeps its saturation, and does not develop a dead spot in the middle.

In CSS you ask for it by putting in oklab at the front of the gradient. OKLCH is the same space in polar form, which means it travels round the hue wheel instead of straight across it, and that gives you a rainbow where OKLab gives you a fade. Neither is more correct; they are different paths between the same two points.

Where the two spaces would land on the same colours, the note under the controls says so. It is measured instead of assumed, by walking both paths and comparing them: two shades of the same blue come out almost identical, and red to green does not.

Older browsers

.hero {
  background-image: linear-gradient(90deg, ...);
  background-image: linear-gradient(in oklab, 90deg, ...);
}

Two declarations, same property. A browser that
cannot read the second keeps the first. No feature
query, no JavaScript, no polyfill.

Interpolation spaces landed in Chrome and Safari in 2023 and Firefox in 2024, so most people have them. For the rest, the fallback option works out the intermediate colours here and writes them into an ordinary sRGB gradient as extra stops.

Seven extra stops per pair is enough that the remaining difference is well under anything the eye can pick up, and few enough that the CSS stays readable. The two declarations then sit one after the other and CSS does the rest: a browser that does not understand the second simply keeps the first.

Banding

A gradient across 1000px with two stops has to
find 1000 intermediate colours. In 8 bit colour
there are only 256 values per channel, so some of
those pixels have to repeat.

That is banding. It shows worst on dark, low
contrast gradients across a wide area, and adding
a faint noise texture on top hides it.

A wide, subtle gradient often shows visible stripes. That is not the gradient being wrong, it is eight bits per channel not being enough steps to cross the distance smoothly. Every renderer has the problem and no amount of extra colour stops fixes it, because the stops are not what ran out.

What does help: adding a very faint noise texture over the top, keeping wide gradients to higher contrast pairs, or using a dithered PNG for the truly subtle ones. Some browsers dither automatically, which is why the same CSS can look clean on one machine and striped on another.

Stops

A new stop takes the colour the gradient already has at that point, so adding one changes nothing until you move it. That makes it safe to click anywhere on the bar and then adjust.

Two stops at the same position give a hard edge instead of a blend, and that is how stripes and colour bands are made. The stripes preset does exactly that, with the repeat switch turned on.

Colours can carry alpha, as eight hex digits. A fade to transparent is worth writing as a fade to the same colour with zero alpha instead of totransparent, because transparent means transparent black, and fading to it in sRGB darkens as it fades.

What the settings share

Copy link packs the whole gradient into a link, so somebody else opens exactly what you built rather than an approximation of it. The gradient rides in the part of the URL after the #, which browsers never send to a server, so the link is readable by whoever you give it to and by nobody in between. Nothing is uploaded and nothing is stored beyond your own browser.