Generative mesh gradients in pure CSS (Stripe-style backgrounds)
Build painterly, generative mesh gradients in pure CSS — animated, deterministic, Stripe-style backgrounds. With seeding for reproducible renders.
The "mesh gradient" — those painterly, multi-color backgrounds you see on Stripe, Linear, and every well-designed SaaS hero of the last three years — looks like it requires a design tool. It doesn't. Three radial gradients, a heavy blur, an SVG noise overlay, and you have one. Add a deterministic seed and you have an infinite supply of unique backgrounds for variant videos.
This is the engineering build: pure CSS, no libraries, reproducible across renders.
Why mesh gradients work
The eye reads two-color linear gradients as "design from 2014." It reads three-or-more-color radial gradients with blur as "this product probably has a Series B." The shift is partly cultural (the look became the standard around 2019) and partly visual — multi-color soft fields feel hand-painted because real painted gradients are not linear.
The look has four components:
- 3–5 radial overlays, each in a different brand color.
- Positioned in non-grid coordinates — never
50% 50%, always something like27% 13%. - Heavy
filter: blur(40-80px)to smear the overlays together. - SVG noise overlay at 8-12% opacity to break banding.
That's the full recipe.
The seeding trick — reproducible gradients
For a video pipeline that renders N variants, you want each variant to have a unique gradient but in a repeatable way. The hack: use the variant's ID as a numeric seed, derive the gradient positions and rotations from it.
function gradientForSeed(seed: string) {
const h = hash(seed); // any string hash → uint32
const pick = (offset: number, range: number) =>
((h >> offset) & 0xff) % range;
return {
c1Pos: { x: 10 + pick(0, 60), y: 10 + pick(8, 60) },
c2Pos: { x: 30 + pick(16, 60), y: 10 + pick(24, 60) },
c3Pos: { x: 30 + pick(32, 60), y: 40 + pick(40, 60) },
rotate: pick(48, 30) - 15,
};
}Plug those numbers into the CSS variables. Every variant with the same seed renders the same gradient; different seeds give different gradients.
The animation: drift, don't dance
The motion on a mesh gradient is the difference between "designed background" and "Windows screensaver." Two rules:
- Use
transform, not background-position. Animatingbackground-positionmakes the gradients visibly slide; animatingtransformon the gradient container makes the whole field drift, which is what you want. - 20+ second loops only. A 5-second mesh animation reads as nervous. A 30-second loop reads as ambient.
The animation we use across landing pages:
.mesh {
animation: drift 24s ease-in-out infinite alternate;
}
@keyframes drift {
0% { transform: scale(1.1) translate(0, 0) rotate(0deg); }
100% { transform: scale(1.3) translate(-3%, 2%) rotate(6deg); }
}The alternate direction is critical. Without it, the gradient snaps back at the end of each loop, which looks like a glitch. With alternate, the motion is a continuous breathe-in / breathe-out.
The noise overlay (the unsung hero)
A mesh gradient without noise on top renders fine in the browser and looks washed-out in MP4. The video codec bands the smooth gradient regions; noise breaks the bands.
<svg class="noise">
<filter id="n">
<feTurbulence type="fractalNoise" baseFrequency=".9" numOctaves="2"/>
</filter>
<rect width="100%" height="100%" filter="url(#n)"/>
</svg>mix-blend-mode: overlay blends the noise into the gradient instead of sitting on top of it. 8–12% opacity is the right range — visible texture without becoming the dominant element.
Rendering to MP4
The mesh gradient is pure CSS, so it renders through the HyperFrames pipeline without special handling. The render is deterministic because the gradient positions are derived from a seed, not from Math.random().
Three practical sizes:
- 1080×1080 — Instagram post background.
- 1080×1920 — Reels / Shorts background.
- 2560×1440 — desktop wallpaper drop or YouTube thumbnail.
Render once per seed; cache the output; reuse across the campaign.
When mesh gradients age
The look will eventually go the way of the "blurry purple-pink" look did. The hedge: use brand-specific colors rather than the default "purple-pink-blue" palette. The structure (mesh + blur + noise) is durable; the specific palette is what dates.
Open the playground, seed your variant IDs, render a background per campaign.
Cite this postBibTeX · APA · Markdown
@misc{okafor2026generative,
author = {Marcus Okafor},
title = {Generative mesh gradients in pure CSS (Stripe-style backgrounds)},
year = {2026},
url = {https://hyperframes.video/blog/generative-mesh-gradients},
note = {HyperFrames blog}
}Marcus Okafor. (2026, April 18). Generative mesh gradients in pure CSS (Stripe-style backgrounds). HyperFrames. https://hyperframes.video/blog/generative-mesh-gradients
[Generative mesh gradients in pure CSS (Stripe-style backgrounds)](https://hyperframes.video/blog/generative-mesh-gradients) — Marcus Okafor, 2026
Marcus leads design and motion at HyperFrames. Before that he shipped editorial motion for newsrooms and product launches. He thinks every easing curve has a personality.
CSS gradient animation that doesn't look like 2014
Animated gradients, mesh gradients, and conic sweeps in pure CSS — five techniques that hold up at video resolution, with MP4 export.
Animated heatmap visualization, rendered to MP4
Build an animated heatmap in plain HTML and CSS — a 12×7 grid where cells fade in with intensity-based color values, scrubbing left-to-right. Deterministic MP4 from JSON.
How to animate your logo (without After Effects)
A logo reveal in pure CSS — spring overshoot, wordmark stagger, and a render straight to MP4. No timeline tool, no plugins.
Building with HyperFrames? Come hang out.
We're on GitHub, in Discord, and the playground is one click away. Bring weird ideas — we collect them.