CSS blob shape generator

Clip an element to an organic outline.

The generator with its code panel set to CSS. Shape the blob, then take a clip-path rule that pins the outline to pixels and a shape() block that states it in percentages, so the clip follows the element as it resizes.

Seed 90 · 6 nodes
.blob {
  /* any size: the clip follows the element */
  clip-path: path("M427,344Q358,438,252.5,433Q147,428,95,339Q43,250,105,177.5Q167,105,266.5,76.5Q366,48,431,149Q496,250,427,344Z");
}
@supports (clip-path: shape(from 0 0, close)) {
  .blob {
    clip-path: shape(from 85.4% 68.8%, curve to 50.5% 86.6% with 71.6% 87.6%, curve to 19% 67.8% with 29.4% 85.6%, curve to 21% 35.5% with 8.6% 50%, curve to 53.3% 15.3% with 33.4% 21%, curve to 86.2% 29.8% with 73.2% 9.6%, curve to 85.4% 68.8% with 99.2% 50%, close);
  }
}

shape() uses percentages, so the clip follows the element size. Older browsers fall back to the path() rule above it.

Overview

One outline, two CSS functions.

A blob is a closed path, and CSS can use that path to decide which part of an element is painted. The export writes the rule twice. First comes clip-path with path(), which every current browser understands and which reads coordinates as pixels. Then comes an @supports block with clip-path and shape(), where the same outline is written as percentages of the element's box.

The two rules describe the identical shape, so the only difference a visitor sees is what happens when the element changes size. The path() version keeps its pixel outline and the element grows out from under it. The shape() version stretches with the box, which is what you want on a card, a hero panel or a picture that has to be responsive.

Design

Clip anything, not just images

The rule applies to any element. A div with a background, a photograph, a video, or a whole card with padding and text inside it all take the same outline.

Code

Two rules, one shape

The fallback and the @supports block are generated together from the same path, so the outline cannot drift between them while you edit.

Layout

Percentages that survive a resize

shape() resolves its coordinates against the reference box, so a blob written once holds up at 240 pixels wide and at 1200 without a media query.

Export

Six ways to take the clip with you.

Every route out of the generator carries the same outline. Pick the one that matches the element you are clipping.

CSS clip-path

The full rule: a path() declaration as the fallback, then an @supports block with the shape() version in percentages. Paste both and keep them together.

shape() on its own

Just the percentage form, for a codebase that has already decided on a browser floor and does not want the pixel fallback in the stylesheet.

SVG and PNG files

The same shape as a vector file, or a PNG at 512, 1024 or 2048 pixels, for places where a CSS rule is not what you need.

SVG markup

One path element in a 0 0 500 500 viewBox. Useful when you would rather clip inside SVG with a clipPath element than in CSS.

A link that rebuilds it

Node count, randomness, seed and color travel in the query string, so the address reopens the identical outline with the code panel on CSS.

React and Flutter

The component and the CustomPainter carry the same geometry, so a shape chosen for the web can be reused in app code without redrawing it.

How it works

Four steps to a clipped element.

Shaping happens exactly as it does on the main generator. Only the code panel changes.

  1. 01

    Shape the outline

    Set Complexity between 3 and 20 nodes and Randomness between 2 and 9, then shuffle until the outline suits the element you have in mind. Fewer nodes clip more predictably.

  2. 02

    Check it at the real size

    Resize the preview to the width the element will have on the page. A shape that reads well at 500 pixels can look flat in a narrow column.

  3. 03

    Copy the CSS

    Take both rules. The path() declaration comes first and the @supports block follows, so a browser that understands shape() overrides the fallback.

  4. 04

    Apply it to the element

    Put the class on the element you want clipped and give that element a size. Clipping does not create dimensions; it only hides what falls outside the outline.

Examples

Four outlines to clip with.

Each link opens the generator with the shape already set and the code panel on CSS, ready to copy.

In depth

Clipping in CSS, function by function.

Two functions that draw the same outline

clip-path takes a basic shape and hides everything outside it. For a circle or an inset that is one short function call, but a blob has no shorthand, so the outline has to be written out. Until recently the only way to do that in CSS was path(), which takes an SVG path string: the same M, Q and Z commands the generator already produces. MDN records path() as Baseline widely available since July 2020, and browser compatibility data puts clip-path with path() in Chrome and Edge 88, Firefox 71, Safari 13.1, Safari on iOS 13, Opera 74 and Samsung Internet 15.

shape() is the newer function, and it describes the same geometry in CSS terms instead of SVG terms. It opens with a from clause that sets the starting point, then a comma-separated list of commands: line to, curve to with one or two control points, arc to, hline, vline, smooth, move, and close at the end. MDN lists shape() as Baseline newly available since February 2026, with support from Chrome 135, Edge 135, Safari 18.4, Safari on iOS 18.4, Opera 120, Samsung Internet 29 and Firefox 148. Caniuse puts that at roughly 87 percent of tracked global traffic, which is why the export keeps the older rule underneath.

The translation between them is mechanical. An SVG quadratic command such as Q476,169,447,249.5 lists the control point first and the end point second; shape() reverses that reading order and writes curve to 89.4% 49.9% with 95.2% 33.8%. The final command is close rather than Z. Nothing about the curve changes, which is the point: the two rules are the same blob written in two grammars.

Percentages against pixels

The practical difference is the unit. MDN is explicit that inside path() every length is implicitly in pixels and no other unit may be used. A path exported at 500 by 500 therefore clips a 500-pixel box exactly and clips a 900-pixel box badly: the outline stays where it was and three sides of the element spill past it. On a narrower box the opposite happens and the clip cuts into the content. The usual workaround is a wrapper at a fixed size, or a transform that scales the element and its clip together, both of which are more machinery than the effect deserves.

shape() accepts a length-percentage in every coordinate, and percentages resolve against the width and the height of the reference box. Divide each SVG coordinate by the viewBox size and the outline becomes proportional: 413 out of 500 is 82.6%, 84.5 is 16.9%, and so on around the shape. The element can now be any size and the blob arrives at the same relative position, which is what makes the rule usable on a fluid card or a hero panel that changes shape between breakpoints.

One consequence is worth planning for: percentages stretch independently on each axis, so a blob clipped onto a very wide, short element is squashed rather than cropped. If the outline has to keep its proportions, give the element an aspect-ratio. The preview is square, so resize it once before committing to a highly irregular shape.

Writing the fallback

The export stacks the two rules in cascade order. The path() declaration comes first and applies everywhere clip-path is understood. The shape() declaration sits inside an @supports block that tests for the function itself, so a browser that parses shape() overrides the pixel version and one that does not simply ignores the block.

Test for the feature, not for a browser. A condition such as @supports (clip-path: shape(from 0% 0%, close)) asks the parser a direct question about a declaration it either understands or does not, and needs no maintenance as support widens. Keep both declarations on the same selector so there is one class to move.

Images, clicks and the text around the shape

Clipping an image is the most common use, and it is the simplest: put the class on the img element, give it a width and an object-fit value, and the photograph is trimmed to the outline with an antialiased edge. Clipping a card works the same way, but remember that the clip hides content rather than reflowing it. Text near the edge of a blob-clipped card will be cut, so add padding generous enough to keep every line inside the narrowest part of the outline, and treat the shape as a frame rather than as a container to fill.

Clipping also changes where the element responds. A clipped element is only interactive inside the visible region: pointer events do not reach the parts that have been clipped away, so the corners of a blob-clipped button are dead. That is usually welcome, because the hit area matches what people can see, but it makes large decorative clipped elements a hazard if they sit on top of a link. Give decorative blobs pointer-events: none and keep them out of the way.

One thing clip-path does not do is affect layout. The element still occupies its full rectangle, and neighbouring text runs along that rectangle rather than following the curve. Flowing text around the outline is a separate property, shape-outside, which takes the same basic shapes and applies them to a float. If you want a paragraph to hug the blob, set shape-outside on the floated element with the same path or shape value you used for clip-path, and the two will agree.

Questions

Clipping questions, answered.

Why does the export contain two clip-path rules?

The first uses path(), which is understood everywhere clip-path is, and reads its coordinates as pixels. The second uses shape(), which takes percentages and scales with the element, and sits inside an @supports block so it only applies where the function is understood. Keeping both means the clip works in every browser and improves where it can.

Which browsers support clip-path: shape()?

MDN lists shape() as Baseline newly available since February 2026, with support from Chrome 135, Edge 135, Safari 18.4, Safari on iOS 18.4, Opera 120, Samsung Internet 29 and Firefox 148. Caniuse puts that at about 87 percent of tracked global traffic today, so the path() fallback still earns its place.

Why does my path() clip break when the element resizes?

path() coordinates are pixels, always. A path drawn on a 500 by 500 grid clips a 500-pixel box and nothing else, so a wider element spills past the outline. That is the problem shape() solves, because its percentages resolve against the element's own box.

Can I clip a card with text inside it?

Yes, but the clip hides whatever falls outside the outline instead of moving it. Add enough padding that every line stays inside the narrowest part of the shape, and check the layout at your smallest breakpoint, where the content is tallest.

Do clicks still work on a clipped element?

Only inside the visible region. Pointer events do not reach the clipped-away parts, so the hit area matches the shape people can see. For a decorative blob sitting over other content, add pointer-events: none so it never intercepts a click.

Get started

Give an element a softer edge.

Shape the outline, copy the two rules, and paste them onto the image or the card you want clipped.