Skip to content
animorio
IT
All components

The freeze card button

Buttons, Physics, TransitionsCanvas, GSAPAdvancedCompressed weight: Demo 11 KBGSAP 28 KBFreeDownload the zip

The Freeze card button of a banking app: pressed, frost really grows over the card and the button from the corners, in branching crystals (diffusion limited aggregation), until everything is pale blue and opaque and says Card frozen. Pressed again, the ice melts from the point that was touched and leaves drops that slide down.

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 freeze card button</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/manrope@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the freeze button of a banking app, under the card it freezes. Pressed, frost grows over the card and the
         button from their corners in branching crystals (drawn in the canvas over both); pressed again, it melts from
         where it was touched and runs down in drops -->
    <div class="freeze" data-freeze>
      <div class="card" data-card>
        <div class="card__face" aria-hidden="true">
          <svg class="card__chip" viewBox="0 0 40 30"><rect x="0.75" y="0.75" width="38.5" height="28.5" rx="6"/><path d="M0.75 10.5h11.5M0.75 19.5h11.5M27.75 10.5h11.5M27.75 19.5h11.5M12.25 0.75v28.5M27.75 0.75v28.5M12.25 15h15.5"/></svg>
          <svg class="card__wave" viewBox="0 0 24 24"><path d="M8.5 8.2a5.5 5.5 0 0 1 0 7.6M12 5.6a9.2 9.2 0 0 1 0 12.8M15.5 3a13 13 0 0 1 0 18"/></svg>
        </div>
      </div>
      <canvas class="freeze__frost" aria-hidden="true" data-frost></canvas>

      <!-- the words of the card, over the frost so that they never go under it -->
      <div class="card__words" data-words>
        <span class="card__kind">Everyday debit</span>
        <span class="card__name">Alex Morgan</span>
        <span class="card__end"><span class="card__end-label">ending</span> 4821</span>
      </div>

      <button class="freeze__btn" type="button" aria-pressed="false" aria-describedby="freeze-live" data-btn data-autoclic></button>
      <!-- the words of the button, over the frost too -->
      <span class="freeze__label" aria-hidden="true" data-label>
        <svg class="freeze__flake" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2.5v19M3.8 7.25l16.4 9.5M3.8 16.75l16.4-9.5M9.4 3.9L12 6.4l2.6-2.5M9.4 20.1L12 17.6l2.6 2.5M4.1 10.6l3.5-.9-.9-3.5M19.9 13.4l-3.5.9.9 3.5M6.7 17.8l.9-3.5-3.5-.9M17.3 6.2l-.9 3.5 3.5.9"/></svg>
        <span class="freeze__words">
          <span class="freeze__main" data-main>Freeze card</span>
          <span class="freeze__sub" data-sub>Tap to unfreeze</span>
        </span>
      </span>
      <p class="sr" id="freeze-live" 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.

26 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 freeze card button” from Animorio and fit it into my site

What it does

On a black background an unbranded debit card shaded from orange to fuchsia to violet, with the chip, the contactless symbol, Everyday debit, Alex Morgan and ending 4821; below it, a dark button as wide as the card with a snowflake and Freeze card. With the mouse over the button the snowflake turns and the first frost appears in the corners of the card and the button. Clicking, the words say Freezing… and from the four corners of the card and the two ends of the button white branching crystals grow, their tips glowing blue as they grow; behind them a bluish haze of frost fills the gaps in patches, the colours of the card fade and the words turn dark blue. When everything is covered a band of light crosses the frost and the button says Card frozen with Tap to unfreeze below. Clicking again the frost melts from the point that was touched, with an uneven edge: behind it a wet film stays for a moment, and many drops slide down in fits and starts leaving a thin trail, down to the bottom edge, where they fade. The card turns warm again and the button says Freeze card.

How it works

A grid and its mask

The card and the button lie on a grid of 2 pixel cells (160 by 139). Every cell knows whether it is on the card, on the button or outside (rounded rectangles), and has a smooth noise value for the patches of the frost and the uneven edge of the thaw.

The crystals: DLA

When the page opens a diffusion limited aggregation runs: walkers start at random on the surface and wander until they touch the frost, and stick there with probability STICKY. To keep it quick a map of distances to the nearest crystal (updated around each cell that sticks) allows long jumps far from the ice; walkers stay on their own surface. It stops when the ice covers BRANCHES of the surface.

An arrival time for each cell

A breadth first walk through the crystal, from the six seeds, gives every crystal cell its distance along the branches: so each branch grows from its root to its tips. A second walk from all crystal cells gives the others the time of the nearest branch plus a delay that grows with distance: the haze comes in behind the branches. The animation is only a threshold T raised by GSAP: fast and reversible.

The drawing and the light

Every frame the grid is written into an ImageData: haze and crystals composited with their alpha, newborn tips in blue, then drawImage scales it up smoothly onto the canvas. The glow is the same picture shrunk and scaled up again, added in lighter mode. The words of the card and the button sit above the canvas and change colour before the frost reaches them, so they always stay readable.

The thaw and the drops

Pressing again every cell gets a thaw time: distance from the touched point plus noise. A second threshold M moves out and erases the frost, leaving a wet film for a moment. Cells that were covered and have just melted give birth to drops: little bodies that stick and slip in fits, with their trail, down to the bottom edge of their surface, moved in requestAnimationFrame with the step capped at 1/30.

Values to change

NameFileDefaultWhat it changes
FREEZEscript.js2.2The seconds the frost takes to cover the card.
MELTscript.js1.6The seconds the thaw takes to cross it.
BRANCHESscript.js0.3How much of the surface the crystals take before the haze fills in, from 0.1 to 0.45.
STICKYscript.js0.55How readily a walker sticks to the frost: less, thicker and bushier crystals.
HAZEscript.js0.78How opaque the frost gets.
DROPSscript.js26The most drops of water running down at once.
GLOWscript.js0.4The blue white glow of the crystals.

Try them in the Playground

When to use it

For the switch that locks a card, an account, a subscription or an access: an important, reversible action, where the frozen state must show at a glance and unlocking should feel like a warm gesture.

On phones

Card and button are 320 pixels wide and fit a 390 pixel screen; below 360 everything shrinks a little. The canvas follows the screen's density (up to 2) and the thaw starts where the finger touched. A pressing finger brings the first frost into the corners like the mouse over it.

With reduced motion

No growth and no drops: the full frost fades in over the card and the button, which say Card frozen; unfreezing, the frost fades away and the card turns warm again.

Pitfalls

  • The frost canvas lies above the button, but the words must lie above the frost: inside the button a z-index was not enough, because GSAP's scale on press creates a new stacking context and the frost covered the words. The button's words are a separate element above the canvas, the real button stays below.
  • White words vanished on the frost and blue ones could not be read on the dark button: they change colour when the frost is about to reach them, not when it is complete, and turn white again only when the thaw has freed their area.
  • The growth added after two add calls in the timeline started at its end, almost two seconds after the click: the threshold tween has an explicit position 0.
  • Taking the crystals' time from the DLA's order of sticking made scattered specks appear here and there: the right time is the distance along the branches from the seeds, so each branch grows from its root.
  • The preview tween for a pressing finger stayed alive and fought the real growth over the same threshold: freezing stops every frost tween before it starts.

Libraries

GSAP 3.15.0, Manrope (font variabile) 5.3.0.

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

Testing

Tested on 5 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