Atmosphere✨ Premium

Blob Morph

An 8-vertex organic SVG blob deforms in real time against a simulated audio feed — cubic Catmull-Rom curves and CSS drop-shadow glow, no canvas, no dependency.

SVGOrganic

You'll get access to the interactive demo with a free account.

This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Atmosphere has 57 effects, including 10 free. Explore the category →

3 usage examples

Hero / Landing — Audio brand visual — Blob Morph example 1

① Hero / Landing — Audio brand visual

WhenHero section of a music brand, podcast, or audio analytics tool — the blob replaces a static illustration.
WhyThe organic animation creates a living presence without abrupt motion, keeping the CTA at the foreground.
Settings240×240 px via CSS (.blob-morph { width: 240px; height: 240px }), baseRadius = 35, data[i] * 20 for expressive deformation, purple-pink gradient.
Product component — Live audio analysis widget — Blob Morph example 2

② Product component — Live audio analysis widget

WhenIn a dashboard or sidebar to visualize an audio stream state, a level, or a pulsing metric.
WhyThe blob conveys 'activity in progress' without numbers — it complements a chart or gauge without duplicating information.
Settings80×80 px (badge size), baseRadius = 28, data[i] * 12 (subtle deformation), teal-cyan gradient, baseFreq = 0.5 for faster response.
Voice Visualizer — AI Assistant & Guided Meditation — Blob Morph example 3

③ Voice Visualizer — AI Assistant & Guided Meditation

WhenAs the main view of a guided-meditation app, a voice assistant, or a speech-to-text tool — the blob fills 70% of the screen and pulses in rhythm with the voice.
WhyAt large size, the Catmull-Rom organic deformation becomes legible and expressive: each vertex reacts to a different simulated audio frequency, producing a spectral breathing effect that is immediately perceived.
Settings240×240 px size (.blob-morph { width: 240px; height: 240px }), baseRadius = 32, data[i] * 18 (expressive deformation), baseFreq = 0.3 (slow organic rhythm), purple-indigo gradient (#c084fc → #4f46e5).

How it works

The effect animates a single SVG <path> whose d attribute is recomputed on every requestAnimationFrame tick. Eight vertices are spaced at equal angles around the viewBox center (50, 50) in a 100×100 coordinate space. Each frame assigns each vertex a radius between 30 and 45 SVG units based on the simulated audio signal.

The getSimulatedAudio generator superimposes two sine waves per vertex: sin(t × freq × 0.002) × 0.5 + sin(t × freq × 0.003 + i) × 0.3, plus 20% random noise. Each vertex's own frequency increases by 0.5 per index (baseFreq + i × 0.5, with baseFreq = 0.3) — vertices oscillate at slightly different rates, simulating a multi-band spectral response.

The 8 computed points are joined by cubic Bézier curves whose control points follow a Catmull-Rom formula at tension 0.2: cp1 = p0 + (p1 − p_{i−1}) × 0.2, cp2 = p1 − (p_{i+2} − p0) × 0.2. The loop closes back to the first vertex (Z), guaranteeing a seamless silhouette with no gap or hard corner.

Visual rendering uses fill: url(#blobGradient) (a radial gradient to define in the SVG <defs>) and filter: drop-shadow(rgba(99, 102, 241, 0.5) 0 0 20px) in CSS — the glow tracks the recomputed outline every frame with no canvas redraw cost.

Accessibility

  • prefers-reduced-motion absent — the animation loops indefinitely even when the OS signals a reduced-motion preference. Add if (matchMedia('(prefers-reduced-motion: reduce)').matches) return; before the first requestAnimationFrame.
  • The <svg> and its <path> carry neither aria-hidden nor a semantic role — screen readers expose the element with a very long d attribute. Add aria-hidden="true" to the SVG.
  • No keyboard interaction or native focus — the effect responds to the RAF only, no user events are handled or documented.
  • The indigo-violet gradient on a dark background yields adequate perceived luminance for a purely decorative element; no text is overlaid in the base demo, so WCAG 1.4.3 does not apply.
  • The #blobGradient is referenced by URL but not defined in the provided snippet — without a <defs> block in the SVG, the fill defaults to black. Always declare the <radialGradient> before use.

Browser compatibility

Requires inline SVG, CSS filter: drop-shadow, and requestAnimationFrame — no canvas, no external dependency.

Chrome 104+✓ Full
Firefox 109+✓ Full
Safari 15.4+✓ Full
Edge 104+✓ Full
Mobile iOS✓ Full
Android Chrome✓ Full

If JavaScript is disabled, the blob stays fixed in the initial shape defined in the HTML <code>d</code> attribute — no JS error, the SVG silhouette remains visible.

The code

HTML structure to paste into your page (CSS + JS available with a premium account):

index.html — structure
<div class="blob-morph" id="blobMorph">
  <svg viewBox="0 0 100 100" aria-hidden="true">
    <defs>
      <radialGradient id="blobGradient" cx="38%" cy="35%">
        <stop offset="0%" stop-color="#a78bfa"/>
        <stop offset="100%" stop-color="#4f46e5"/>
      </radialGradient>
    </defs>
    <path id="blobPath" d="…"/>
  </svg>
</div>
🔒 Unlock the full code — from €2.99 the first month

Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
points = 8 8 Number of radial vertices (JS constant). 5–6 = very round blob, 10–12 = more complex, detailed silhouette.
baseRadius = 30 30 Rest radius in SVG units (100×100 viewBox). 20 = compact, 40 = large (watch for viewBox clipping).
data[i] * 15 15 Maximum deformation amplitude in SVG units. 8 = subtle, 25 = highly expressive.
baseFreq = 0.3 0.3 3rd argument to getSimulatedAudio — base oscillation rate. 0.1 = slow and organic, 0.8 = fast and nervous.
* 0.2 (Catmull-Rom tension) 0.2 Bézier control-point tension factor. 0.1 = very smooth, 0.4 = more angular curves.
.blob-morph { width / height } 120px Rendering box size via CSS. The SVG scales proportionally — no JS change needed.
fill: url(#blobGradient) indigo gradient Blob color via SVG <radialGradient> in <defs>. Change stop-color for your palette.
filter: drop-shadow(…) rgba(99,102,241,.5) 0 0 20px Glow color and radius via CSS. Align the RGBA color with your base gradient for a coherent look.

FAQ

Replace getSimulatedAudio with an AnalyserNode read from the Web Audio API: analyser.getByteFrequencyData(dataArray), then normalize (dataArray[i] / 255). Pass this array in place of the simulated values — the Bézier machinery stays identical.
Not natively — getElementById('blobPath') targets a single element. For multiple instances, give each <path> a distinct id (blobPath2, blobPath3…) and refactor animateBlobMorph to accept the element as a parameter rather than using a global reference.
The RAF handle is not stored in an exposed variable. Add let rafId; rafId = requestAnimationFrame(animate); and call cancelAnimationFrame(rafId) to stop. Alternatively, the if (!blobPath) return; guard halts the loop if you remove the #blobPath element from the DOM.