SVG Shape and Blob Generators: Auditing the Emitted CSS and SVG

You clicked “Generate” on a blob shape generator and it handed you a wall of CSS. That wall is almost always the same shape. A WebKit-prefixed -webkit-clip-path: polygon(...) with no unprefixed fallback, a second clip-path: url(#blob) that references an inline SVG sitting in your HTML, and a dozen inline style attributes on the <svg> that do not need to be there. The generated path itself is the bigger problem. Most tools approximate the smooth blob you see on screen with 40 to 80 straight-line polygon vertices. It renders, but it costs you paint time on every scroll, and it breaks the moment you try to animate it.

What a Typical Blob Generator Actually Emits

Open any free blob tool and the output has the same skeleton. Here is a representative sample, shortened to six vertices but structurally identical to what Blobmaker or Softr’s generator will paste into your clipboard:

.blob {
  -webkit-clip-path: polygon(29% 11%, 68% 4%, 88% 29%, 91% 68%, 63% 92%, 19% 78%);
  clip-path: polygon(29% 11%, 68% 4%, 88% 29%, 91% 68%, 63% 92%, 19% 78%);
}

That looks fine. Now open the same tool’s “SVG” tab and you get the second output format:

<svg viewBox="0 0 100 100" style="width: 100%; height: auto; display: block;" aria-hidden="true">
  <path d="M29,11 C44,2 59,1 68,4 C77,7 84,18 88,29 C92,40 94,55 91,68 C89,77 76,88 63,92 C47,97 29,89 19,78 C11,69 8,48 11,36 C14,24 20,16 29,11 Z"
        style="fill: #4B9CD3; stroke: none;"/>
</svg>

Read that d attribute carefully. The generator wrote a cubic Bézier path with control points that do not match the visible curve. The C commands use control points pulled from the same random seed as the polygon, so the path is not actually smoother than the polygon version. It is the same shape, wrapped in a different syntax, and the parser has to do more work to rasterise it. The inline style attributes are pure bloat. fill: #4B9CD3 belongs in your stylesheet. stroke: none is the default for SVG paths. display: block on the SVG is a reasonable reset, but it does not belong in the markup.

Write The Path By Hand

The hand-written equivalent takes one line of CSS and one line of SVG, with the presentation moved out:

.blob {
  clip-path: path('M29,11 C44,2 59,1 68,4 C77,7 84,18 88,29 C92,40 94,55 91,68 C89,77 76,88 63,92 C47,97 29,89 19,78 C11,69 8,48 11,36 C14,24 20,16 29,11 Z');
}
<svg viewBox="0 0 100 100" aria-hidden="true">
  <path d="M29,11 C44,2 59,1 68,4 C77,7 84,18 88,29 C92,40 94,55 91,68 C89,77 76,88 63,92 C47,97 29,89 19,78 C11,69 8,48 11,36 C14,24 20,16 29,11 Z" class="blob-fill"/>
</svg>

That is the whole difference. The generator gave you a polygon pretending to be a curve, then a path with unnecessary inline styles. Replace both with a single clip-path: path() and a class.

Here is the failure case no tool documents: if you paste the polygon version into an older Firefox, the clip-path property is ignored entirely because the polygon is unprefixed but the engine only shipped the prefixed version until a later release. The page shows the full rectangle. That is not a subtle bug. It is a visible layout break, and the fix is a @supports guard.

The Paint Cost of The Generated Clip-Path

Using the CSS Triggers categorisation, clip-path on a blob with 60+ vertices is a medium paint cost. It does not trigger layout. It does not trigger compositing on its own. But every time the shape repaints, scroll, hover, a sibling moving, the engine has to re-rasterise the clipped region. More vertices mean more path segments the rasterizer walks. A 12-vertex polygon is cheap enough to ignore. A polygon with many vertices on a low-power Android device will visibly stutter during scroll.

The polygon() function has another cost that nobody mentions on the tool page: the engine stores each vertex as a percentage coordinate, and computing the final pixel position for each one is not free. The path() version stores absolute coordinates in user units, which the rasterizer consumes directly. For a static shape, the difference is theoretical. For a shape inside a position: sticky header or a component that animates, you will feel it.

The @supports Fallback for Engines That Reject path()

The unprefixed clip-path: polygon() is Baseline since September 2017, so you do not need a fallback for the polygon form. The path() function inside clip-path is a different story. It is not Baseline. Chrome shipped it in January 2021, Safari in March 2020, but Firefox only landed it in February 2022. If you target Firefox ESR or any release older than that, the clip-path: path(...) declaration is dropped and the component renders unclipped.

The correct pattern is a @supports guard that tests the exact syntax you intend to use:

.blob {
  /* Fallback: no clip, the element shows as a rectangle */
}

@supports (clip-path: path('M0,0 L100,0 L100,100 Z')) {
  .blob {
    clip-path: path('M29,11 C44,2 59,1 68,4 C77,7 84,18 88,29 C92,40 94,55 91,68 C89,77 76,88 63,92 C47,97 29,89 19,78 C11,69 8,48 11,36 C14,24 20,16 29,11 Z');
  }
}

Notice what the fallback is: nothing. No clipped shape, no rounded rectangle, no SVG mask as a second choice. The accepted fallback for clip-path is no clipping at all. Do not try to fake it with border-radius or a mask-image. A rectangle is the honest answer. Your layout must survive an unclipped region, which means the background and content need to look acceptable without the organic shape.

Accessibility Tree Implications

When a generator tells you to paste an inline <svg> as a decorative blob, it usually adds aria-hidden="true". That is correct for a purely decorative shape. But here is what the tool does not tell you: if the SVG has no role and no aria-label, and you forget aria-hidden, the entire <path> becomes a node in the accessibility tree. Screen readers announce something like “graphic” with no name. That is noise. Keep aria-hidden="true" on the SVG and never put text inside the blob’s path.

A worse mistake is using the SVG as a container for text. Some generators offer a “put text inside the blob” option. That produces an SVG <text> inside the clipped region. Screen readers may or may not read SVG text depending on the engine and the assistive technology pair. The reliable pattern: render the text as an HTML element next to the SVG, not inside it, and let CSS position it over the blob. Then the text is in the accessibility tree as normal content.

Data URI vs Inline SVG vs External Reference

Generators offer three ways to ship the blob: an external SVG file, an inline <svg> in the HTML, or a url() data URI in the CSS. The external file is the worst choice for a decorative shape. It adds a network request. The data URI is best for a one-off shape. It keeps the SVG out of the DOM and the accessibility tree entirely. The inline SVG is the middle ground: it adds DOM nodes but lets you animate the path from JavaScript.

Here is a data URI version of the same blob, ready to drop into your CSS as a background:

.blob-bg {
  background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><path d="M29,11 C44,2 59,1 68,4 C77,7 84,18 88,29 C92,40 94,55 91,68 C89,77 76,88 63,92 C47,97 29,89 19,78 C11,69 8,48 11,36 C14,24 20,16 29,11 Z" fill="%234B9CD3"/></svg>') no-repeat center / contain;
}

The %23 is the URL-encoded hash for the hex colour. If you copy-paste the generator’s output straight into a data URI, the # in #4B9CD3 terminates the URI and the background silently breaks. That is a classic failure mode. Fix it by using %23 in the CSS or by writing the SVG with fill="currentColor" and setting the colour from the CSS.

When The Generator Is The Right Tool

Use a blob generator when you need a one-off shape fast and you do not care about the path quality. The output is fine for a static decoration on a marketing page. Do not use it when you need to animate the blob, when the shape sits in a critical above-the-fold component on a content-heavy page, or when you are building a design system that other developers will copy. The generator’s output does not document the fallback, does not warn about the paint cost, and does not check the accessibility tree. You have to add all of that yourself.

The hand-written path shown here is a cubic Bézier path with 6 control point pairs. A generator gives you 12 to 16 vertices in the polygon version. For the same visible curve, the hand-written path has fewer commands and renders faster. The tradeoff is time: writing a smooth blob path by hand takes practice. A good compromise: generate the path, then simplify it with a path optimisation tool that removes redundant control points without changing the shape. That gets you the best of both.

Animating The Blob Without Breaking The Compositor

Animating clip-path between two polygon values is only possible when both polygons have the same number of vertices. The engine interpolates each vertex independently. That works, but it is a main-thread paint animation. The compositor does not handle clip-path animation, so you lose the smoothness you would get from animating transform or opacity. The compositor-only properties are transform and opacity. Everything else, including clip-path, runs on the main thread and will jank if the main thread is busy.

If you need a blob that morphs smoothly, do not animate clip-path. Put the blob in an SVG and animate the d attribute of the <path>. SVG path animation is a different mechanism and it runs in the SVG renderer, not the CSS pipeline. That is the honest answer to “how do I animate my blob”.

Content Visibility and The Blob

If the blob is below the fold, add content-visibility: auto to its container. That tells the engine to skip layout and paint for the shape until it scrolls near the viewport. The content-visibility property pairs well with a clip-path because it prevents the expensive paint work from happening at page load. A generated polygon with many vertices is exactly the kind of content that benefits from this. Without it, the engine paints the blob even when the user never scrolls to it.

What The Generator Never Tells You About Transfer Size

Every inline style attribute in the generated SVG is bytes over the wire. The data URI version gzips well because it is a single string, but the raw SVG with style="fill: #4B9CD3; stroke: none;" is not compressible beyond the repetition. The gzip transfer size difference between a clean SVG and a generator’s output is small, a few hundred bytes, but it adds up across a page with five blobs. The real cost is the DOM: five inline SVGs with five <path> nodes each is ten extra nodes the engine has to parse and the accessibility tree has to walk.

The Fallback That Is Not A Fallback

Do not use clip-path: url(#blob) as a fallback for clip-path: path(). The url() form references an SVG <clipPath>, and it has its own support story. Safari and Chrome support it, but Firefox has been inconsistent with clip-path: url() on HTML elements. The reliable fallback for an unsupported path() is no clip. If you absolutely need a clipped shape in an older engine, use mask-image with the SVG as a mask. That is a different property with its own fallback chain, and it is not what the generator outputs.

Performance Benchmarks and The Honest Number

There is no official benchmark that measures “blob generator output” because the output varies. The qualitative paint cost of clip-path is medium when the polygon has more than 50 vertices, according to the CSS Triggers categorisation. Below 20 vertices it is low. A hand-written cubic Bézier path with 6 control points is low. The number to remember is 50. If your generated polygon has more than 50 vertices, simplify it. The visible difference is negligible and the paint cost drops measurably.

The real gap between the generated polygon and the hand-written path is not the shape. It is the maintenance. A polygon with many vertices is unreadable. A path with 6 C commands is something a developer can edit. When your design changes from a teardrop to a rounded square, the polygon version is a wall of numbers you cannot reason about. The path version you can redraw in ten minutes.

What To Replace and What To Keep

Here is the practical checklist. Replace the WebKit-prefixed -webkit-clip-path with the unprefixed clip-path. The prefix is unnecessary in every engine released since 2017. Replace the inline style attributes on the SVG with a CSS class. Replace the high-vertex polygon with a path() that uses the same visual curve. Keep the aria-hidden="true" on the SVG. Add the @supports guard for path() if you support Firefox ESR. Add content-visibility: auto to the container if the blob is below the fold. Delete the stroke: none because it is the default. Delete the display: block from the SVG and set it in your CSS reset.

The one thing you should never keep is the -webkit-clip-path without the unprefixed version. That is the single most common mistake in generator output. Every engine that supports the unprefixed property has to parse the prefixed one first, find it valid, and then apply it. It works, but it is dead weight. The unprefixed property has been Baseline for seven years.

Blob Shape Generator Clip-Path Output: What You Are Actually Deploying

When you paste a generator’s output into production, you are deploying three things: a polygon approximation, a prefixed property, and a missing fallback. The polygon approximation is the visible shape. The prefixed property is a legacy marker that newer engines ignore. The missing fallback is the hole in your layout for any engine that does not support path() or does not support the unprefixed polygon().

For older Firefox releases, the unprefixed clip-path: polygon() is not supported. The engine shipped only -webkit-clip-path until a more recent version. That means a user on an older release sees the rectangle. The @supports guard for polygon() is simple:

@supports (clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%)) {
  .blob {
    clip-path: polygon(29% 11%, 68% 4%, 88% 29%, 91% 68%, 63% 92%, 19% 78%);
  }
}

That guard is the minimum. The generator did not write it for you. Add it.

The One-Sentence Rule for Blob Shapes

A blob shape generator is a time-saver for a throwaway decoration, and a liability for anything you expect to maintain. The output is a snapshot of a random seed. It has no semantic meaning, no animation story, and no fallback. Treat it as a starting point, not a final asset.

Inline Style Audit

The inline styles in generated SVG are the first thing to audit. Every style="fill: #..." and style="stroke: none" is a declaration that belongs in a stylesheet. The audit process is mechanical. Open the SVG, delete every style attribute, move the fill to a CSS rule, delete stroke if it is none, and delete display if it is the default. The result is a cleaner DOM and a smaller transfer size.

The viewBox attribute is the one thing you keep. It defines the coordinate system for the d attribute. Without it, the path coordinates are interpreted in a default 300x150 viewport, which distorts the shape. The generator’s viewBox="0 0 100 100" is correct. Keep it.

Hand-Written SVG Path vs Generated: The Real Difference

A hand-written path is shorter, readable, and editable. A generated path is longer, opaque, and fixed. The visible shape is often identical. The difference is what happens when the design changes. A designer asks you to make the blob longer on the left. With a hand-written C command, you move one control point. With a generated high-vertex polygon, you regenerate and hope the random seed gives you something close to what you had. That is the real gap.

The d attribute of a hand-written path uses relative or absolute C commands. The generator’s output uses the same syntax but with control points that do not align to any visible feature of the shape. That is why the path looks random. It is random. The generator seeded a set of points and connected them with a smoothing algorithm. There is no intent behind the curve.

CSS Clip-Path Blob Performance: What It Costs

The performance of a clip-path blob is a function of the number of vertices and the size of the shape. A small blob with 12 vertices is free. A large hero blob with many vertices paints slowly on a mid-range phone. The paint cost is medium because the engine has to build a clip region and then rasterise the component inside it. That is not a layout cost and not a compositing cost, but it is a paint cost, and paint is the most expensive phase of the rendering pipeline.

The will-change property does not help here. Setting will-change: clip-path tells the engine to promote the shape to its own layer, which is a different mechanism. It does not make the paint faster. It moves the component to the compositor, where the clip still needs to be rasterised on the main thread. Do not add will-change to a blob. It wastes memory and does not solve the problem.

The Fallback for Unsupported Browsers: No Clip

The fallback for clip-path: path() is no clipping. That is the entire fallback. There is no way to fake an organic shape with border-radius that looks acceptable. border-radius produces a geometric ellipse, not a blob, and the viewer notices the difference instantly. Accept the rectangle. Make sure the background and content look reasonable without the clip. That is the production-ready approach.

If you cannot accept a rectangle, you have two options. First, use mask-image with an inline SVG. That is a separate property with its own support chain. Second, use an SVG <clipPath> referenced by url(). That works in Chromium and WebKit but has inconsistent Firefox support. Neither is what the generator outputs, so you are writing it yourself.

Tooling: Lightning CSS and The Minifier’s Role

Lightning CSS, the Rust-based minifier, will not fix your generator output. It minifies the CSS, strips comments, and merges rules. It will not remove the -webkit- prefix because the prefix is valid and Lightning CSS targets the syntax, not the redundancy. It will not inline the SVG. It will not add the @supports guard. The minifier’s job is to reduce bytes, not to change behaviour. Your job is to change the behaviour before the minifier touches it.

The minifier does help with the data URI version. It compresses the whitespace in the URI and may shorten the path coordinates if they are reducible. That is a real saving, but it is a rounding error compared to the size of the page’s images.

FAQ

Why does my blob generator output a polygon instead of a path? The polygon is cheaper for the generator to compute. It places random points and connects them with straight lines. A path with cubic Bézier curves requires a smoothing algorithm that is more code to write and more math to run. The generator optimises for its own speed, not for the quality of the output.

Can I animate a blob generator’s polygon output? Only if you keep the same number of vertices and never change the structure. The engine interpolates each vertex independently. Adding a vertex mid-animation is not possible with CSS. Use SVG path animation instead, which is a different technique.

What is the safest fallback for clip-path: path()? No clip at all. The component renders as a rectangle. That is the accepted fallback in every engine that does not support path(). A rectangle is not pretty, but it is honest and it does not break the layout.

Is the WebKit prefix still needed for clip-path? No. The unprefixed clip-path is Baseline since September 2017. Every engine that supports the property supports the unprefixed form. The prefix is dead weight. Remove it.

Should I use an external SVG file for the blob? No. An external file adds a network request and a render-blocking resource. Use a data URI in CSS for a one-off shape, or an inline SVG in HTML if you need to animate the path. Both are faster than a separate file.

The Single Most Practical Next Step

Open the generator’s output right now and delete the -webkit-clip-path line. That is the one change that makes the output closer to production-ready with zero risk. Then add the @supports guard for path() if you use it. The rest, the inline styles, the path simplification, the accessibility audit, is refinement. The prefix removal is the difference between shipping a legacy artifact and shipping modern CSS.