← rafapages.com

Gradient Studio

Recolor the three blobs, preview the animation, then generate standalone HTML.

Original design by Marina Budarina, Figma Community file "Animated gradients"

Standalone animation HTML, ready to paste into a .html file:

The generated file is fully self-contained (no dependencies, no external assets).

Made something with this?

It's free to use, no attribution required. If you ship it somewhere, tag it, and I'll feature it here.

🏷️ tag it, I'm looking
How it works technical notes

Quick start

Using it on your site

The output has no dependencies: no JS libraries, no build step, no external images or fonts. Drop the <div class="gradient-card">…</div> block and its <style> straight into any page. To resize it, just override the width/height on .gradient-card; the SVG paths scale with it since preserveAspectRatio="none" stretches to fill.

Browser support & performance

The animation is SMIL (native SVG <animate>), supported in every modern browser (Chrome, Safari, Firefox, Edge) with no JavaScript running after page load. It respects prefers-reduced-motion by disabling the animation for users who've asked for less motion. The blur is a CSS filter, so it's GPU-accelerated like any other CSS effect.

License & credit

Free to use for anything, commercial included, no attribution required. The design itself is from Marina Budarina's Figma Community file; credit to her ( @marina_uiux) is appreciated if you're sharing where it came from.

The technical deep dive

Curious how a Figma file becomes an animated SVG? Here's the full pipeline.

1 · Where the artwork comes from

The animation originates from a Figma Community file, “Animated gradients”. The design is three frames of one card, each a snapshot of a soft gradient mid-motion. Figma's Smart Animate tweens between them, so the three frames are really keyframes of a single loop.

.fig file (ZIP) canvas.fig DEFLATE schema + ZSTD node tree Kiwi decode vector paths + transforms SVG morph

A .fig is a ZIP. Inside, canvas.fig is Figma's binary fig-kiwi container: an 8-byte fig-kiwi magic, a version, then length-prefixed chunks. Chunk 1 is the compiled Kiwi schema (raw DEFLATE); chunk 2 is the document's node tree (Zstandard-compressed in newer files). Decoding the schema lets you decode the message into a tree of nodes: frames, groups, and vectors.

2 · The three keyframes

These are rendered live below from the same path data the studio uses, not images, but the actual decoded vectors drawn statically:

3 · Anatomy of one card

Each frame is a 612×367 card (40px corners, a 10px inside white stroke) over a #141516 base. Inside are two stacked layers, each holding the same three blob shapes:

Every blob is just a filled Bézier path under a heavy blur. In Figma the group carries a FOREGROUND_BLUR of radius: 180, i.e. a blur radius of 90. The blur is applied in CSS on each layer (filter: blur(90px)) rather than with an in-SVG feGaussianBlur. Safari/WebKit miscomputes SVG filter regions when the card is stretched to a different aspect ratio (and can freeze the filter mid-animation), which leaves the blobs clipped or static; CSS blur works in screen pixels and re-renders every frame, so it stays correct and portable across browsers.

4 · Extracting the geometry

Each vector's fillGeometry points at a commands blob, a little binary path program: one tag byte (1=move, 4=cubic, close at the end) followed by float32 coordinate pairs. Decoding it yields an SVG path. The blob's coordinates are local to the vector, so each point is run through the vector's transform and then its group's transform to land in the frame's coordinate space (0–612 × 0–367):

framePoint = groupTransform( vectorTransform( localPoint ) )

The transforms here are pure translations, which is exactly why the blobs appear to glide: Smart Animate is just moving the same shapes around between frames.

5 · How the morph runs in the browser

No JavaScript animation here, it's SMIL. Each blob is one <path> with an <animate> on its d attribute, cycling through all three frames and back to the first:

<path fill="#F6C944" d="…frame 1…">
  <animate attributeName="d" dur="13.5s" repeatCount="indefinite"
    calcMode="spline" keyTimes="0;0.333;0.666;1"
    keySplines="0.45 0 0.25 1; 0.45 0 0.25 1; 0.45 0 0.25 1"
    values="…f1…;…f2…;…f3…;…f1…" />
</path>

Path interpolation only works if every values entry has the same command structure. Because the blobs are the same shapes translated, their command counts match across frames (7 / 6 / 9 commands for the three blobs), so the browser interpolates each control point linearly, reproducing Smart Animate exactly. The keySplines apply an ease-in-out to each segment.

6 · What the studio does

The page embeds the decoded path data for all three frames. Changing a color re-renders the two SVG layers with the new fills (each blob's color is shared between the base and dodge layers, as in the source). Generate HTML assembles a complete, dependency-free document with your colors baked into the paths, the exact same markup you see in the preview.