How to svg blob performance and animation reference
- Step 1Profile the real shapes — Because there is no seed, profile the exact blobs you will ship. Open Chrome DevTools → Performance, record a scroll over the section, and look for paint records and frames over 16ms. Capture and freeze the shapes before profiling so results match production.
- Step 2Prefer CSS filter blur over SVG filters — Apply softness with CSS
filter: blur()on the blob element, not an in-SVGfeGaussianBlur. CSS filters on compositor-layer elements are hardware-accelerated; SVG filters generally are not. The generator emits no filter, so you are choosing CSS by default — keep it that way. - Step 3Promote only what moves — Add
will-change: transform(ortransform: translateZ(0)) to blobs you animate or that sit over a scrolling area, to lift them onto their own compositor layer. Do not blanket-apply it — each promoted layer costs GPU memory. - Step 4Animate transform/opacity, never layout — For movement, animate
transformandopacityonly — these are compositor-only. Morphing the pathdrepaints but does not reflow as long as the SVG box stays fixed (it does: 400×400). Never animatewidth,height,top, orleft. - Step 5Budget by point count — Keep morphing blobs at Points 6–8 for safe 60fps everywhere. Reserve Points 10 (and API-only 11–12) for a single non-animated accent. The more
Csegments, the more per-frame interpolation work. - Step 6Honour reduced motion — Wrap morph and drift in
@media (prefers-reduced-motion: reduce)and disable them, leaving the static blob in place. Removing the blob can shift layout; an opacity pulse (scale, not shape) is usually acceptable even under reduced motion.
What costs what
Relative GPU/CPU cost of the techniques people use on blobs. The blob SVG itself is essentially free; the treatment is where the cost lives.
| Technique | Where it runs | Relative cost |
|---|---|---|
Rendering the static <path> | Rasteriser (once) | Negligible — file is < 1 KB |
CSS filter: blur() on a compositor layer | GPU | Low–moderate; scales with blurred area |
SVG feGaussianBlur | Mostly CPU / not composited | Higher — avoid for animation |
transform/opacity animation | Compositor (GPU) | Low — preferred for movement |
CSS d-property morph | Main thread repaint | Scales with C segment count |
Morph cost by Points value
Segment count = Points − 1. Frame-rate guidance is for mid-range mobile; flagships are far more forgiving.
| Points | Cubic `C` segments | Morph at 60fps on mid-range mobile? |
|---|---|---|
| 3–5 | 2–4 | Yes, trivially |
| 6–8 (typical) | 5–7 | Yes |
| 10 (slider max) | 9 | Usually; many at once may drop frames |
| 11–12 (API only) | 10–11 | Risky for simultaneous morphs — test |
Cookbook
Copy-paste performance patterns. The blob path comes from the generator; these are the CSS and profiling habits that keep it smooth.
Compositor-promoted, blurred background blob
Promote the blob so its blur is GPU-accelerated and it does not repaint when the page scrolls behind it.
.blob {
position: absolute; z-index: 0; pointer-events: none;
width: 200%; opacity: 0.18;
filter: blur(90px);
will-change: transform; /* lift onto its own layer */
transform: translateZ(0);
}Cheap drift (compositor-only)
Move the blob with transforms — no morphing, no repaint, runs on the GPU.
@keyframes drift {
0%, 100% { transform: translate3d(0,0,0) rotate(0deg); }
50% { transform: translate3d(0,-20px,0) rotate(5deg); }
}
.blob { animation: drift 12s ease-in-out infinite; }Morph budget — keep counts equal and low
Two Points-7 blobs (7 C segments each) morph at 60fps. Equal counts are required; low counts keep it cheap.
/* both blobs generated at Points 7 → matching command counts */
@keyframes blob {
0%,100% { d: path('M…7C…Z'); }
50% { d: path('M…7C…Z'); }
}
.blob path { animation: blob 9s ease-in-out infinite; }Reduced-motion contract
Disable motion but keep the blob. Layout stays put; sensitive users are respected.
@media (prefers-reduced-motion: reduce) {
.blob,
.blob path { animation: none !important; }
}
/* a gentle opacity/scale pulse is generally acceptable: */
@media (prefers-reduced-motion: reduce) {
.blob.pulse { animation: pulse 6s ease-in-out infinite; }
}
@keyframes pulse { 50% { opacity: .25; transform: scale(1.02); } }Spot the jank in DevTools
What to look for when a blob section stutters on scroll. Freeze the real shape first — there is no seed to recover it later.
DevTools → Performance → record a scroll over the blob section
• Long frames (>16ms) on the main thread → blur repaint
fix: will-change: transform on the blob
• Paint flashing on the blob during scroll
fix: promote to a compositor layer (translateZ(0))
• Layout (purple) entries while morphing
fix: never animate width/height/top/leftEdge cases and what actually happens
SVG feGaussianBlur used for an animated blob
performance riskIn-SVG feGaussianBlur is generally not compositor-accelerated, so animating around it lands work on the main thread. The generator emits no filter — keep it that way and apply CSS filter: blur() on the element instead, which is GPU-accelerated on a promoted layer.
Large blurred blob repaints on every scroll frame
paint stormA big filter: blur() region not on its own layer can repaint as the page scrolls, blowing the 16ms frame budget. Promote it with will-change: transform (or translateZ(0)) so it composites independently of the scrolling content.
will-change applied to every blob
memory wasteEach promoted layer consumes GPU memory. Blanket will-change: transform on dozens of static blobs can exhaust memory and slow things down. Promote only blobs that animate or sit over a scrolling region; leave static decoration alone.
Animating width/height/top/left during morph
layout thrashThese properties trigger layout reflow every frame. Because the generator's SVG has a fixed 400×400 box, morphing d alone does not reflow — but if you also animate geometry properties you reintroduce thrash. Use transform/opacity for all movement.
Mismatched point counts morphed in plain CSS
invalid morphThe CSS d property needs equal command counts. Two blobs from different Points values cannot interpolate and the browser renders broken mid-frames. Generate both at the same Points value, or use GSAP/Flubber to match counts.
Many high-Points blobs morphing at once
frame dropsPer-frame morph cost scales with C segments and multiplies across simultaneous animations. A grid of Points-10 morphs will drop frames on mid-range mobile. Cap concurrent morphs, use transform-drift for background blobs, and profile on a real mid-range phone.
Animation runs despite reduced-motion preference
accessibility failContinuous morph/drift can trigger discomfort. Always disable via @media (prefers-reduced-motion: reduce) while keeping the static blob. A small opacity/scale pulse (no shape change) is generally tolerated, but shape morphing and large drifts should stop.
Profiling a placeholder shape, shipping a different one
invalid benchmarkNo seed means a 'representative' blob is not the blob you ship. Capture and freeze the exact shapes, then profile those. Performance scales with command count, so a higher-Points production blob can behave worse than the one you tested.
Backdrop-filter blur stacked behind blobs
performance riskbackdrop-filter: blur() (e.g. a frosted card over blobs) is among the most expensive effects and compounds with the blob's own blur. Limit backdrop-filter regions, keep them small, and test scroll performance on mobile before shipping the combination.
Resizing the blob by regenerating
ExpectedYou never need to regenerate to resize — the 0 0 400 400 viewBox scales to any CSS width/height. Regenerating only changes the shape (and, with no seed, gives a new one). Resize in CSS to avoid losing the shape you profiled.
Frequently asked questions
What is the GPU cost of filter: blur() on a large blob?
CSS filter: blur() on a compositor-layer element samples a radius of pixels per output pixel; cost scales with the blurred area and radius. Modern desktop GPUs handle a 60–90px blur in a couple of milliseconds, but a large blurred region on low-end mobile can consume most of the frame budget. Promote the blob to its own layer and keep the blurred area in check.
Should I use CSS blur or SVG feGaussianBlur?
CSS filter: blur() on the element. It is hardware-accelerated when the element is on a compositor layer; in-SVG feGaussianBlur generally is not and lands work on the main thread. The generator emits no filter at all, so CSS is the natural choice — keep softness in your stylesheet.
When should I use will-change on a blob?
When the blob animates (drift/morph) or sits over a scrolling region and you see paint-on-scroll in DevTools. will-change: transform lifts it onto its own compositor layer. Do not apply it to every static blob — each layer costs GPU memory, and over-promotion can hurt more than it helps.
Does morphing the blob cause layout reflow?
No, as long as the SVG element's box is fixed — and the generator's output is fixed at 400×400. Changing d repaints the path but does not reflow the page. Trouble only appears if you also animate width/height/top/left; keep movement to transform/opacity.
At what point count do morph animations get expensive?
Cost scales with C segments, which equals Points − 1. Points 6–8 (5–7 segments) morph at 60fps on virtually all devices. Points 10 (9 segments) is usually fine alone but can drop frames when many morph at once. The API-only 11–12 should be tested before shipping animated.
Why should I profile the actual blob and not a sample?
Because there is no seed — every generate produces a different shape, and a higher-Points production blob has more segments (and thus more morph cost) than a low-Points sample. Capture and freeze the exact shapes you will ship, then profile those for an accurate budget.
How do I keep a blurred blob from janking on scroll?
Promote it to its own compositor layer with will-change: transform or transform: translateZ(0) so its blur composites independently of the scrolling content. Also keep the blurred area reasonable — very large blurred regions are the usual culprit behind scroll repaints.
What is the recommended prefers-reduced-motion pattern?
Default: full morph/drift. Reduced motion: disable the animation but keep the static blob (removing it can shift layout). A subtle opacity/scale pulse — not a shape change — is generally acceptable even under reduced motion. Wrap it in @media (prefers-reduced-motion: reduce).
Are blob SVGs themselves a performance concern?
No. Each blob is a single <path> under 1 KB; rendering the static shape is negligible. The cost is always the treatment — blur area and morph segment count. Optimise those, not the file. If you do want to shave bytes, svg-pro-minifier trims the markup.
Is backdrop-filter blur over blobs a problem?
It can be. backdrop-filter: blur() is one of the most expensive effects, and stacking it over already-blurred blobs compounds the cost. Keep frosted regions small, limit how many are on screen, and profile scroll performance on mobile before shipping the combination.
Can I animate the blob's colour cheaply?
Animating fill or opacity is cheap. Animating a gradient's stop-color is also fine. The generator only outputs a solid fill, so any colour animation is CSS you add. Keep colour animation separate from shape morphing so each stays easy to budget. See the animation techniques guide.
Do I need to regenerate the blob to resize it?
No. The 0 0 400 400 viewBox scales to any CSS size, so set width/height in your stylesheet. Regenerating would change the shape (no seed) and could change its segment count, invalidating your performance testing. Resize in CSS only.
Privacy first
Every JAD SVG tool runs entirely in your browser using the DOM API and Canvas. Your SVG files never leave your device — verified by zero outbound network requests during processing.