Mesh Gradient Generator
Soft overlapping radial gradients that read like a mesh gradient, previewed at hero size with text on them, and reproducible from a seed.
Enable JavaScript to change the gradient; the CSS below is for the defaults.
The last colour given is also the base, so the gaps between the blobs read as part of the gradient rather than as holes.
How far each gradient reaches. Large is the point: a blob smaller than the element shows its edge, which reads as a circle rather than as a mesh.
The positions come from this, so the same seed is the same gradient tomorrow and in the published page.
A hero, with words on it
Large on purpose: a mesh gradient reads completely differently at the size it will actually be used, and the text is here because something will be.
.hero {
background-color: rgb(245 158 11);
background-image: radial-gradient(at 1% 11.7%, rgb(30 86 236 / 0.9) 0px, transparent 70%), radial-gradient(at 84.8% 10.3%, rgb(124 58 237 / 0.9) 0px, transparent 70%), radial-gradient(at 20% 81.9%, rgb(226 69 107 / 0.9) 0px, transparent 70%), radial-gradient(at 86.9% 95.1%, rgb(245 158 11 / 0.9) 0px, transparent 70%);
}
/* Seed 7: the positions come from it, so this
gradient is reproducible rather than a lucky refresh. */
A mesh gradient in a design tool is a warped grid of colour, and CSS has no equivalent. What CSS does have is several large, soft radial gradients over a base colour, which reads as the same thing at the sizes people actually use it: a hero, a card, a section background.
Two details separate that from a muddy blur. The gradients have to be much larger than the element, so no edge of a circle is ever visible. And the base colour has to be one of the mesh colours rather than white, or the gaps between the blobs read as holes.
The positions come from a seed rather than from randomness, which means the same seed is the same gradient tomorrow, in the published page, and in the server-rendered first paint. “New seed” changes the seed rather than the output directly, so what you pick is reproducible instead of being a lucky refresh.
How to use
- Pick a layout and four or five colours. The last one is also the base.
- Set the blob size high enough that you cannot see any circle edges. 70 percent is a sensible start.
- Press “New seed” until the arrangement is right, then note the seed number that comes with the CSS.
Example
.hero {
background-color: rgb(245 158 11);
background-image: radial-gradient(at 1% 11.7%, rgb(30 86 236 / 0.9) 0px, transparent 70%), radial-gradient(at 84.8% 10.3%, rgb(124 58 237 / 0.9) 0px, transparent 70%), radial-gradient(at 20% 81.9%, rgb(226 69 107 / 0.9) 0px, transparent 70%), radial-gradient(at 86.9% 95.1%, rgb(245 158 11 / 0.9) 0px, transparent 70%);
}
/* Seed 7: the positions come from it, so this
gradient is reproducible rather than a lucky refresh. */
Four gradients, one base colour, no image. The preview is deliberately large, because a mesh gradient at thumbnail size tells you nothing about how it will look behind a headline.
Pitfalls
Small blobs show their edges. Below about 40 percent you are looking at circles rather than at a mesh. Larger and softer nearly always reads better.
White gaps read as holes. That is why the base is the last colour rather than the page background. If the gradient looks patchy, the base is wrong.
Text on top needs checking. A gradient has a lightest point and a darkest point, and a contrast checker can only test one colour at a time. Check the text against both extremes.
Too many colours turns to mud. Three or four is usually the limit before the blobs average out into grey in the middle. Five works when they are close in hue.
Keep the seed. It is the only thing that makes a gradient you liked reproducible. It is written into the CSS as a comment for that reason.
It is not a mesh gradient. A real one warps a grid; this approximates the look with radial gradients. For anything that has to match a design file exactly, export an image.
Large blurred areas cost paint. Not much, and not nothing. On a page with several of them on a phone it is worth checking.
Compatibility
radial-gradient with an at position and multiple background images works in every browser in use, so the
output needs no fallback.
The positions come from a small integer sequence seeded by the number you choose, and that sequence is implemented identically in JavaScript and in PHP. The mirror comparison runs on every build, which is what guarantees the server-rendered first paint is the same gradient rather than a similar one that shifts when the page hydrates.
The tests pin the properties that matter: the same seed gives the same positions, different seeds give different ones, every blob stays within ten percent of the element’s bounds, and the base colour is one of the mesh colours rather than white.
Colours run through the same helper as the shadow tools, so a solid colour prints as rgb() with no alpha
and a partly transparent one prints with a slash alpha.
Frequently asked questions
Why does my gradient look grey in the middle?
How do I get the same gradient again?
Can I animate it?
Will it work as a full-page background?
body, and it costs a little paint on a large viewport. Check on a phone before shipping it behind a
long page.