Bhinish Dhiman
How to make an SVG wave or pattern background that stays readable and small
Generate the shapes from a seed you keep, so the same link redraws the same file, and keep them out of the headline's way. Measure the text against the least legible pixel behind it, not the average, and add a scrim layer where it fails. Run the SVG through SVGO, inline it as a percent-encoded data URI while it stays under about 4 KB, and use whole-pixel tiles with edge copies so a pattern repeats without a seam.
Waves behind a hero, a blob in each corner, a dotted texture under a pricing table: decorative backgrounds are judged on a canvas with nothing on them. The page they ship on puts a headline across the crest of the third wave, crops the artwork to a phone, and repeats a tile two hundred times. Each of those has a number you can check before the file leaves your editor.
Make one with the tool
- Open the SVG background generator. It starts on the Dusk waves preset, already measured, so there is a background with text on it straight away.
- Choose Waves, Peaks, Blobs or Pattern, or press one of the swatches under Presets, every one measured.
- Shape it. For waves and peaks set Layers, Points across, Swing and Reach, and Grow from the bottom or top edge. For blobs set Blobs, Lobes, Wobble and Size, and pick Corners to keep the middle clear. For a pattern pick the kind, then Tile size and Mark size.
- Press New seed for a different shape with the same settings, and Back to the last seed if you preferred the old one.
- Set the colours, or paste a link from the palette generator into Use a palette: the darkest colour becomes the background.
- Read the worst pixel under the preview. If a line fails, press Set the scrim to …%, the lightest that passes, or change Text colour to test.
- Check On other screens, and choose Crop to cover or Stretch under On a different shape of screen.
- Copy the CSS, Custom properties, Tailwind v4 or Inline style; save the SVG file; or, under As an image, set Width, px and Height, px and press Make the PNG.
A background is many backgrounds
Worst-pixel contrast is the contrast between the text colour and the single pixel behind the text that it differs from least. Over a flat colour every pixel is the same, so any pixel will do. Over waves, blobs or a pattern, the text crosses several colours, and the one that decides whether a line can be read is the closest to the text colour, wherever it sits.
The average is the figure that misleads. Measured with the tool’s engine over a white background with grey dots:
| Text #475569 over white with #64748b dots | WCAG 2 ratio |
|---|---|
| Over the white paper | 7.58:1 |
| Averaged across the area | 6.04:1 |
| Over a dot | 1.59:1 |
The average says the text passes the 4.5:1 that WCAG asks of body text. Over every dot it fails by a wide margin, and a reader sees letters dissolve into the texture. The readout reports the worst pixel and can mark it on the preview.
There are three fixes, in order of cost. Move the shapes: lower Reach so the waves stay below the headline, or use Corners for blobs. Change the text colour: the readout offers black or white when one of them measures better over every line. Or add a scrim, a flat layer of black under light text, or white under dark text, at the lightest opacity that lifts the worst pixel to the threshold. The scrim is written into the exported CSS as a linear-gradient layer above the image, so it ships with the background rather than living in a separate rule.
The shape has to survive the link
Most generators make a shape from random numbers and forget them. Measured on 8 October 2026, Haikei’s editor kept no parameters in its address, and reloading fffuel’s leaves generator at the same address drew a different file, 16,399 bytes and then 17,580. The only copy of the shape you liked is the file you downloaded.
A seed is the number a pseudo-random generator starts from: the same seed gives the same sequence, so the same shapes. The tool keeps the seed and every setting in the link, after the #, and each layer draws from its own stream, so adding a fifth wave leaves the first four where they were. Open the link on another machine next year and the same bytes come out.
Smaller files, measured
An SVG written by a generator is rarely as small as it could be. The same optimiser, SVGO, run with settings that keep the viewBox:
| File | As exported | After SVGO |
|---|---|---|
| Haikei layered waves (editor) | 2,660 bytes | 1,383 bytes (−48%) |
| fffuel llleaves (default) | 16,399 bytes | 10,111 bytes (−38%) |
| This tool’s default waves | 1,301 bytes | 976 bytes (−25%) |
Part of the difference is how layers close. Haikei’s waves trace the bottom edge back with 35 flat curves; a closing edge needs two straight lines. The tool prints both sizes, gzipped as well, for every change you make.
Then decide where it lives. A percent-encoded data URI puts the SVG inside the CSS as text, with only %, #, < and > escaped. For the default waves that is 1,039 bytes of CSS, 503 gzipped. Base64 makes the same file 1,330 bytes, 732 gzipped, because it hides the repetition gzip relies on. Under about 4 KB, inlining saves a request; above that, a separate file is cached once and shared across pages, so the tool switches its advice.
Patterns that repeat without a seam
A pattern ships as one tile repeated by background-size. A tile has two ways to show its edges. A mark that crosses the right edge is cut off, and the same mark never appears on the left, so every repeat shows a hard line. And a tile sized in fractions of a pixel lands on a different sub-pixel each time, which can open a hairline gap.
The tool uses whole-pixel tiles and copies every mark that crosses an edge to the opposite edge. Then it checks: it paints the pattern as an endless plane across four tiles and compares it with the tile repeated, pixel by pixel. With the copies switched off, a 40-pixel confetti tile differs by 208 of 255 levels at the seam; with them, by at most 1. Every pattern is drawn by the tool itself, so there is no attribution to carry: Hero Patterns, for comparison, is licensed CC BY 4.0, which asks for credit wherever it is used.
Other screens crop or distort
An artwork drawn at 16:9 meets a phone at roughly 9:19.5. With background-size: cover the curves stay true, but a 390 × 844 screen shows only 26% of the artwork’s width. With 100% 100% and preserveAspectRatio="none", every edge stays in view and each curve is squashed 3.85 times. Neither is wrong; the readout puts the number next to each choice for four screen shapes so you can pick one knowingly, or draw a 9:16 artwork for phones.
Other ways to do it
There is no built-in operating-system method for generating these. You can draw the shapes in a vector editor and export SVG, then optimise the file from a terminal with npx svgo input.svg -o output.svg. Neither step measures the text that will sit on the result.
Check that nothing left your device
The tool runs in the page, and the optimiser is loaded from this site rather than a third party. Open your browser’s developer tools, choose the Network tab and work for a minute: no request carries your colours or shapes, and the state lives in the fragment, which browsers never send.
Where to go next
Open the SVG background generator and start from a preset. For a smooth background behind a headline, the gradient generator measures text the same way; to shrink an SVG you already have, use the SVG optimiser.