Skip to content
animorio
IT
All components

The ink bleed button

Buttons, Hover, Physics, SVGCanvas, GSAPAdvancedCompressed weight: Demo 13 KBGSAP 28 KBFreeDownload the zip

A pill of cotton paper with a tooth and fibres, and over it a fountain pen that follows the pointer, a drop of ink swelling on its nib. At a click the drop falls, bursts into a crown and the ink creeps out by capillarity along the fibres, with a branching front, until the whole button is soaked: there Added to bag shows, pale on the dry ink. A second click takes it out of the bag and the paper comes back clean.

The code

A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN. The React and Vue tabs hold the same demo as a ready component, which switches its animations off when it leaves the page.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>The ink bleed button</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/opsz.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/opsz-italic.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the ink bleed button: a pill of cotton paper. Over it a fountain pen follows the pointer and a drop of ink swells
         on its nib; a click lets the drop fall, it bursts into a crown and the ink creeps out along the paper's fibres
         until the whole button is soaked, with Added to bag pale on the dry ink. A second click bleaches it clean -->
    <div class="ink" data-ink data-autoclic>
      <button class="ink__btn" type="button" aria-label="Add to bag, €18" data-btn>
        <!-- the paper and the printed words; the ink, drawn on a canvas; the stain, the pale words, the grain and the pen -->
        <svg class="ink__layer ink__layer--back" aria-hidden="true" data-back></svg>
        <canvas class="ink__layer ink__soak" aria-hidden="true" data-soak></canvas>
        <svg class="ink__layer ink__layer--front" aria-hidden="true" data-front></svg>
      </button>
      <p class="sr" aria-live="polite" data-live></p>
    </div>
  </main>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script src="script.js"></script>
</body>
</html>

Playground

Change values, colours and text with the controls and watch the animation respond live. Or write in the code yourself. Then download the folder with your changes.

18 values to change

Or ask Claude Code for it

With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:

Take “The ink bleed button” from Animorio and fit it into my site

What it does

On a white background a pill button of cotton paper, with a tooth and a few pale fibres, saying Add to bag €18. With the mouse over it, a red lacquered fountain pen with a gold nib takes the pointer's place, held a little above the paper with its soft shadow on it: it follows the pointer on a spring, leans when it is dragged, and on the tip of the nib a drop of blue ink swells and swings when the pen starts and stops. Clicking, the drop lets go and falls on the spot, and the lightened pen springs up for a moment; the drop bursts into a crown with thin spikes and droplets, and from there the ink spreads into the paper by capillarity: the front is branching and ragged, runs faster along the fibres and across than up and down, slows down as it goes, has a light halo of water ahead of it and a darker line of pigment on its edge; behind it the blue dries to blue-black. When the paper is soaked through, Added to bag shows pale on the dry ink with a tick. A second click takes it out of the bag: from the point clicked a pale front runs along the fibres again, the ink pales and fades and the paper comes back clean with Add to bag. On a phone the pen comes down where the finger touches with its drop ready, lets it fall and leaves.

How it works

The pen and the drop on the nib

The fountain pen is a hand-drawn SVG (lacquered barrel, ring, gold nib with its slit and breather hole) that chases the pointer on a spring integrated by hand on GSAP's ticker; its lean follows the horizontal speed. The drop on the tip grows in SWELL seconds and hangs on a second spring, pushed by the pen's acceleration: it swings when the pen starts and stops. The shadow on the paper moves and widens with the pen's height.

The paper as a grid

The pill is a grid of cells, one per pixel. Each cell has a cost for the ink: a noise stretched across like the sheet's grain, raised to an exponent with BRANCH (higher, more difference between easy and hard cells, so more branches), and divided along the fibres, which are the same curves drawn in the grain. Going up and down costs more than across: the fibres lie across.

The front by capillarity

From the point where the drop falls, Dijkstra's algorithm finds for each cell how far the ink must travel along the cheapest way: the front that comes out of it branches, with fingers along the fibres and bays where the paper is hard. It is worked out a slice per frame while the drop falls, so the click does not stutter. Then the front moves over that map by Washburn's law: the distance grows with the square root of the time, fast at first and slower and slower.

Wet, dry and halo

Every frame the canvas is painted again from the map: ahead of the front a light halo of water, on the front a darker line where the pigment gathers, behind it wet blue that turns blue-black in DRYING seconds, a little darker where the ink pooled in a fibre. The crown on top is a path redrawn every frame (a wobbling circle, SPIKES bell-shaped spikes and droplets) with a fine turbulence filter.

Taking it out of the bag

At the second click another map is worked out from the point clicked, on the same paper: a pale front runs along the same fibres, and where it passes the ink pales to a light blue and fades in UNDO seconds, until the paper is clean again. Added to bag goes at once; Add to bag is printed underneath on the paper and comes back with it.

Values to change

NameFileDefaultWhat it changes
SWELLscript.js0.9The seconds the drop takes to swell on the nib.
SOAKscript.js1.8The seconds the ink takes to creep through the whole button.
DRYINGscript.js0.6The seconds the ink stays wet blue after the front has passed.
FIBRESscript.js1How much the ink runs along the paper's fibres: 0 not at all, 2 in long fingers.
BRANCHscript.js1How ragged and branched the front is: 0 a smooth blot, 2 very ragged.
SPIKESscript.js13How many spikes the crown of the drop has.
UNDOscript.js1.1The seconds the paper takes to come back clean at the second click.
LABELscript.jsAdd to bagThe words on the button.
PRICEscript.js€18The price beside them.
ADDEDscript.jsAdded to bagThe words on the dry ink.

Try them in the Playground

When to use it

For the main button of a shop or a site where paper and ink belong: fountain pens, stationers, letterpress printers, publishers, calligraphers, writing paper. It suits an action with an outcome that stays (add to bag, subscribe, sign), because the ink marks it and the second click erases it.

On phones

The pill is 320 pixels wide, or the screen minus 48, and the paper is worked out again for the real size. With no pointer passing over it, at a touch the pen comes down where the finger touches with its drop ready, lets it fall there and leaves; the second touch pales the ink from that point.

With reduced motion

The pen does not follow the pointer: it fades in, still, right of the words, with its drop ready. At a click there is no fall and no moving front: the dry ink with the crown and Added to bag appear together in a fade, and the second click fades them away the same way.

Pitfalls

  • Dijkstra's algorithm over the whole button (over twenty thousand cells) costs some twenty milliseconds: done at the click it made the fall stutter. Now it runs in slices of seven thousand steps per frame while the drop falls, and the front starts only once the map is done.
  • A front moving at constant speed looked like a mechanical wave: with Washburn's law (distance as the square root of time) it sets off quickly and slows down like real ink.
  • Without a different cost across and up and down the branching front stayed round overall: going up and down costs more, because the fibres lie across, and the stain stretches along the button.
  • Clipping the canvas with the CSS border-radius left stepped corners: the pill is cut inside the canvas itself, with a soft edge one pixel wide.
  • The fibres in the cost and those in the grain must be the same: with two different sets the ink made fingers where the paper showed no fibres.
  • A click with no pen over the button (a quick tap, a script's click, the keys) must first bring the pen down with its drop, or there is nothing to fall.

Libraries

GSAP 3.15.0, Fraunces (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 13 KB; the libraries: GSAP 28 KB.

Testing

Tested on 4 October 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: Done

License

Use it in your own projects and in your clients’ projects. Do not resell it or redistribute it as is. GSAP stays under its own Standard license. Full license.

All components