Ink Bloom
Ink blooming in water: a plain radial gradient, pushed through a feDisplacementMap driven by fractal turbulence, becomes a stain with ragged edges that grows and breathes. Six different seeds guarantee no two blooms ever look alike.
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. Backgrounds has 50 effects, including 6 free. Explore the category →
3 usage examples
How it works
feDisplacementMap whose map is a feTurbulence in fractalNoise mode, 3 octaves. The displacement (scale 62) twists the gradient's outline: smooth edges become fingering, exactly like the front of one liquid pushing into another. Six identical filters with different seeds run in parallel, and each new bloom draws one at random.
The animation then oscillates the baseFrequency around a base value memorised per filter — never by reading and rewriting the current value, which would turn the frequency into a random walk over minutes. The fractal edges therefore breathe in place, without ever running away.Accessibility
- The trigger is a real
<button>covering the panel, with an explicitaria-label: the effect is keyboard-operable — Enter or Space blooms a stain at the centre — not mouse-only. - Under
prefers-reduced-motion: reduce, blooms appear with a simple fade, with no growth phase and no edge breathing. The effect keeps its meaning (a stain appears where you click) while dropping the animation. - Two palettes ship: one in
multiplyblending for a light paper ground, one inscreenfor a dark one. Picking the right pair avoids stains that turn dirty grey through wrong compositing. - The pool is capped at 14 blooms; beyond that the oldest are removed. That bounds how many SVG filters run at once, which protects both performance and the panel's legibility.
Browser compatibility
SVG feTurbulence and feDisplacementMap filters, CSS radial gradients, mix-blend-mode, plus requestAnimationFrame and matchMedia. No library, no canvas.
Without JavaScript the opening composition is not laid down and the panel stays empty: give .ink-scene a background (paper or ink). If SVG filters are unsupported, blooms render as plain radial gradients — less organic, but perfectly presentable.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<!-- The 6 displacement filters live in a zero-sized <svg>,
placed anywhere in the page (see the full code). -->
<div class="ink-scene" id="inkScene">
<!-- Isolated layer: this is what receives the blooms -->
<div class="ink-canvas" id="inkCanvas"></div>
<!-- Full-panel button: click = bloom at the cursor, Enter/Space = bloom at centre.
A button, not a div: the effect stays keyboard-operable. -->
<button class="ink-trigger" id="inkTrigger"
aria-label="Bloom a drop of ink — click to place, Enter to centre">
</button>
</div>
Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
feDisplacementMap scale |
62 | The primary control. At 20 the edges barely ripple, at 62 they form real fingering, past 100 the stain breaks apart into separate filaments. |
Turbulence baseFrequency |
0.066 | How finely the outline is cut. A low frequency gives a few large lobes; a high one, tight lace that reads more like blotting paper than water. |
numOctaves |
3 | Richness of the fractal detail. 1 gives a smooth undulating outline, 4 or 5 very irregular edges — at the cost of a markedly heavier filter. |
| Gradient stops | 80 % / 55 % / 18 % / transparent | The pigment's density profile. Bringing the stops together gives a crisp-edged stain; spreading them, a diffuse wash that fades out gradually. |
| Bloom size | 28 to 58 % of the width | Range drawn at random on each bloom. Narrowing it gives a regular composition; widening it, a mix of large pools and small drops. |
| Pool cap | 14 blooms | How many stains coexist before the oldest are removed. It is also the performance guard: each stain keeps an SVG filter alive. |
| Palette and blend mode | paper (multiply) / dark (screen) | multiply for dark ink on a light ground, screen for luminous ink on a dark one. Mismatching the pair gives dull stains. |
FAQ
feTurbulence with the same seed always produces exactly the same noise. With a single filter, every bloom would share a rigorously identical outline up to scale — and the eye spots that repetition immediately. Six distinct seeds are enough to break the impression: short of comparing two stains side by side, you never notice they come from a finite catalogue.baseFrequency and adding a small variation each frame turns the oscillation into a random walk: after a few minutes the frequency has drifted far from its original value and the stain no longer looks like anything. So the code memorises each filter's base and always computes base + oscillation, never current + delta.