Digitopia Labs

Gradient generator

Build a gradient with real grain — the 5 to 15 per cent band that stops output reading as machine-made. Free, no sign-up, nothing to install.

Build a gradient

Drawn with CSS, not WebGL. Nothing is sent anywhere.

  1. Colour 1 of 5: #2A7F7B
  2. Colour 2 of 5: #F2EFE9
  3. Colour 3 of 5: #16324F
  4. Colour 4 of 5: #E4C05A
  5. Colour 5 of 5: #E2725B

Between 5 and 15% is the band that stops output reading as machine-made. Below 5 it looks rendered. Drag it to nothing and watch the wash band.

Seed 41k·7c2. The seed decides which colour pools where and which two ends the wash runs between — same seed, same picture, on any machine.

  • Grain is 7%, inside the 5–15% band. This slider stops at 15%, so it can drop below the band and cannot climb above it — in the mode the two grain layers add up and their sum can.
  • The wash under the pools holds at most 185 distinct 8-bit colours across 1600px, so each one is at least 8px wide — wide enough to read as a band on a large screen. Grain is what breaks those edges up.
  • This picture is CSS: the colours, the rotation, the scale and a real turbulence tile. Distortion, swirl and motion are not approximated here, because a picture that pretends to be the shader is worse than one that says which parts it left out.

Why a generated gradient looks generated

It is not the colours. A purple-to-indigo ramp is a cliché of its moment, but you can build a gradient from four colours nobody has used together and it will still read as machine-made, because the giveaway is the surface rather than the palette. A CSS gradient is arithmetic between two stops: every pixel is exactly the value the interpolation says it should be, and nothing in the physical world looks like that. Film has grain, paper has tooth, ink has bleed, a photographed sky has sensor noise. A perfectly smooth field of colour is a thing only a computer makes, and after two years of everything on the internet being made that way, the eye now reads smoothness as a signature.

Which is why the one control on the tool above is grain rather than a colour picker. Drag it to zero and the picture does not become a different gradient — it becomes the same gradient, obviously rendered. Put it back and the colours have not changed at all; what has changed is that the surface now has a texture, and the field stops announcing how it was produced.

Five to fifteen per cent, and what is on either side of it

The band is narrow and both edges of it matter. Below 5% there is not enough noise to break the interpolation up: the grain is present, measurable in the file, and doing nothing you can see. Above 15% it stops reading as texture and starts reading as a fault — the image looks like a bad upload, or like a photograph taken at an ISO nobody chose. In between, the field looks like a surface rather than a calculation, and almost nobody can tell you why.

The slider above runs from 0 to 15%, which is the range of one grain layer, so on this page you can fall out of the bottom of the band and cannot climb out of the top. The mode has two layers — a mixer that sits in the colours and an overlay that sits on top of them — and it is their sum that the band is about, so there the ceiling is reachable and worth reaching for once, to see what noise looks like.

The grain here is real noise rather than a pattern of dots: an SVG feTurbulence tile, fractal noise at three octaves, desaturated and blended into the colours rather than laid over them as a grey film. It is tiled at a fixed 160px, and that number does not scale with the picture on purpose. Grain that grows with the image is not grain — it is a texture, and a texture that doubles in size when you export at twice the resolution has stopped being the thing you chose. Eleven per cent at 4K has to be the same physical speckle as eleven per cent here, or the setting means nothing outside the window it was set in.

The other thing grain fixes, with the arithmetic

sRGB gives each channel 256 values, and a gradient cannot invent a value between two of them. So the number of distinct colours a two-stop ramp can hold is fixed by its widest channel step: a wash from #16324F to #F2EFE9 moves red by 220, and can therefore contain 221 colours and no more. Spread those across a 1600px canvas and each one occupies a run of about seven identical pixels. A run of identical pixels with a visible edge on each side is what banding is, and it is why large, gentle gradients — exactly the ones people use as hero backgrounds — are the ones that show it worst. The tool above counts this for whichever two colours the seed has put at the ends of its wash, and prints the figure floored, so it understates rather than overstates.

Browsers do dither gradients to some extent, and how much depends on the engine, on whether the layer was composited on the GPU, and on the display it lands on. That is the argument for adding your own noise rather than hoping: a grain layer you set is the same on every machine, and it dithers those edges by scattering neighbouring values across them, which the eye integrates back into the step in between. The texture and the fix are the same layer, which is a pleasant accident rather than a design.

What this page does in CSS, and what needs a GPU

The picture above is genuine CSS: overlapping radial pools of your colours over an angled linear wash, plus the turbulence tile. That means the colours, the rotation and the scale are all really applied, and you could paste the result into a stylesheet and get the same field. What it is not is a mesh: there is no distortion, no swirl and no motion, because those are shader operations and CSS has no equivalent to approximate them with.

The alternative would be to run the shader here, and the reason not to is the same reason this page has no hero animation and no fonts from a third party. A visitor who searched for a gradient generator wants a gradient, not a WebGL context, a shader compile and a canvas repainting every frame while they read. So the tool renders the picture the mode itself shows on a machine with no GPU, and the difference is stated rather than hidden — which is the same rule the mode’s CSS export follows when it declines to pretend a stylesheet can carry a moving mesh.

What a gradient cannot tell you

Whether text will be legible on it. A contrast ratio is defined between two colours, and a gradient is not one colour — it is a different colour at every pixel, so a heading laid across it has a different ratio at its first letter and its last. There is no single figure to report and no honest way to grade it, which is why this app’s own controls over the gradient canvas sit on a solid ink plinth rather than floating translucently on the picture: white on that plinth is 17.89:1 whatever the shader does underneath, and a tool that grades other people’s colour cannot ship a control it is unable to grade itself.

If text has to go over a gradient, the workable answers are to put it on a solid or near-solid panel, or to grade it against the LIGHTEST point it crosses and the DARKEST point it crosses and require both to pass. The contrast checker will do either pair for you. What no gradient tool should do is print one ratio for a background that does not have one.

When a still frame isn’t the picture

This page draws one still mesh in CSS, with one grain layer and one control. Gradient mode is the same gradient on the real shader: four styles, the six or seven parameters each one can actually drive — distortion, swirl, twist, bands, softness, noise — both grain layers rather than one, scale and rotation and speed, and motion you can pause and scrub. Your own colours go in from the palette instead of the five here, the seed button reshuffles which colour pools where, and the export sheet gives you CSS, a Tailwind v4 utility, the React component with the props it is rendering with, the document as JSON, or a raster still at up to 4K.

Run this gradient on the shaderIt opens on these 5 colours, in this order, at 7% grain, on seed 41k·7c2 — the link carries your gradient in the address, not a default. The picture there is the shader’s rather than this CSS one, and it moves.

Opens Gradient mode. Free, no sign-up, and it runs in your browser like this page does.