Skip to content
animorio
IT
All components

The candle button

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

The Log out button of an account on a black page, lit by a candle burning in the round end of the pill. The flame is a shape drawn every frame on a canvas: it breathes, flickers at the tip and leans on a spring towards the pointer coming near, as if in a breath of air; its warm light falls on the pill, the dish and the words. At a click a brass snuffer comes down, the flame dies under the bell, a thread of smoke rises from the wick curling in curl noise and the pill goes cold and dark: Signed out. The second click strikes a match, brings it to the wick and the light comes back: Welcome back, Noor.

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 candle button</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the candle button: the Log out button of an account, with a lit candle in the round left end of the pill, on a
         black page. The flame breathes and leans to the pointer coming near, and its warm light falls on the pill. A
         click brings down a brass snuffer, the flame dies under it, a thread of smoke curls up from the wick and the
         pill goes dark; another click strikes a match that lights the wick again -->
    <div class="candle" data-candle>
      <button class="candle__btn" type="button" data-btn data-autoclic>
        <span class="candle__fill" aria-hidden="true"></span>
        <canvas class="candle__art" aria-hidden="true" data-art></canvas>
        <span class="candle__words">
          <span class="candle__word" data-word>Log out</span>
        </span>
      </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.

30 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 candle button” from Animorio and fit it into my site

What it does

On a black background a dark pill 60 pixels tall, warmed from the left by an amber light that makes it look like wood by candlelight, with a warm halo just past its rim. On the right, in cream Mona Sans, Log out; in the round left end a short candle in a brass dish: the wax is an ivory cylinder with a run down its side, the top glows through the wax where the flame melts it, and on top lies the pool of melted wax. Over the black wick the flame: a teardrop with a blue foot, an orange body, a pale heart lower down and a soft bloom of the same shape. On arrival the flame, just lit, climbs to its height with a small flare. The flame breathes slowly, flickers at the tip and sways by itself; the pointer coming near bends it towards itself, and passing quickly the air it moves makes it waver, then it rights itself with a sway; the light on the pill and the glints on the dish tremble with it. Over the pill the words widen and the fill swells with a bounce. At a click the words become Logging out…: from the upper right a brass snuffer comes down along its rod, the bell settles on the wick with a small bounce, the flame chokes and dies, the light on the pill goes; the bell lifts and from the wick, still glowing for a moment, a thread of grey smoke rises, thin at the bottom and wider and softer higher up, curling in the air for three seconds. The pill turns cold and grey, the wax unlit, and the words say Signed out, then Sign in. The second click: Signing in…, a match comes in from the upper right and flares in the air, comes down in a small arc to the wick, the wick catches and the flame climbs from its blue foot to its full height, past it and back; the match leaves, shaken, going out with its own wisp, and the warm light comes back on the pill: Welcome back, Noor, then Log out. With the keyboard the focus shows an amber ring and the flame gives a start. On a phone a finger near the candle bends the flame and a tap puts it out or lights it again.

How it works

The flame: a shape on a bent spine

The flame is a polygon built on a spine of 18 points: from foot to tip the spine moves by (lean · s² + flick · s³) · height, so it bends more and more towards the tip; the width is (1 − s)^1.25 for the tapered tip and a root near the foot for the round bottom. The same shape is filled four times in lighter mode: a wide soft bloom with shadowBlur, the body with a vertical gradient from blue to orange, the pale heart shorter and narrower, the blue cup at the foot. Never a glowing round spot: even the bloom has the flame's shape.

Breath, flicker and the spring

The height follows a slow wave of BREATH a second plus two octaves of gradient noise, and the tip flickers with fast noise. The lean is a damped spring (1.6 swings a second, damping 0.32) towards a target: the pointer near pulls the flame towards itself with a force that falls with the cube of the distance (REACH is where it is half), and its horizontal speed adds the breath of the air it pushes. So the flame goes a little past and comes back, like a real one.

The smoke: a ribbon in curl noise

The wick gives off seventy particles a second, in order. Each rises by its warmth and is carried by the curl of a two-dimensional gradient noise field that drifts in time: the curl, worked out by finite differences, is a flow without divergence, so the air turns and curls without piling up. The ribbon is drawn by joining consecutive particles in runs of four, each run a single path of quadratic curves: thin and pale near the wick, wider and fainter as it rises, fading towards the edges of the canvas.

The snuffer and the match

They are two GSAP timelines on the same objects the loop draws. The snuffer comes down along its rod with back.out, the flame chokes (height and width shrink), the bell lifts with power2.in and the smoke starts as it rises, with the ember fading. The match fades in from the edge, flares (1.55 times its flame, then 1), comes down in an arc, and the wick catches with elastic.out; its flame always stands upright whatever the stick's angle and leans back against its motion.

The light on the pill

Every frame the light follows the flame (plus the match's) with a small lag and its flicker, and is written into three CSS variables: --glow sets the opacity of the warm gradients over the pill and of the halo, --out that of the cold dark coat, --lit mixes the colour of the words with color-mix. The dish, the wax and the glow through the wax use the same value, so the whole scene warms and cools together.

The states

A real button with aria-disabled while it goes out or lights, an aria-live region saying Logging out…, Signed out., Signing in… and Signed in. Welcome back, Noor. The pill is as wide as the longest of the six phrases, measured already wide. The lit flame always moves, so the loop runs while it is lit and in view; in the dark, once smoke, snuffer and match are done, it stops.

Values to change

NameFileDefaultWhat it changes
LOG_OUTscript.jsLog outThe words while signed in.
LOGGING_OUTscript.jsLogging out…The words while the candle is put out; the aria-live region says it too.
SIGNED_OUTscript.jsSigned outThe words once the candle is out.
SIGN_INscript.jsSign inThe words in the dark, offering the way back.
SIGNING_INscript.jsSigning in…The words while the match lights the candle.
SIGNED_INscript.jsWelcome back, NoorThe welcome. The name is made up: in a real site it is the user's.
SAIDscript.js1The seconds Signed out or the welcome stays before the button offers the way back.
SNUFFscript.js0.7The seconds the snuffer takes to come down on the flame.
SMOKEscript.js3How many seconds the wick keeps smoking.
CURLscript.js1How much the air curls the smoke: 0 it rises straight, 3 it twists a lot.
BREATHscript.js0.35The flame's slow breaths a second.
LEANscript.js1How far the flame leans to the pointer: 0 it does not feel it.
REACHscript.js170How far away the pointer is felt, in pixels: at this distance the flame leans half as much.

Try them in the Playground

When to use it

For signing out of an account, or in, on a site with a warm, night-time tone: a restaurant, a wine bar, a candle or perfume brand, a journaling or meditation app, a fiction or role-playing site; also as a switch for the dark theme. The sign out has to be wired to your own: here it is all simulated.

On phones

The pill is about 250 pixels wide and fits at 390 with room to spare; the smoke, the snuffer and the match stay above the candle and left of the words. A finger resting near the candle bends the flame; a tap puts it out or lights it again.

With reduced motion

The flame stands still, with no breath or flicker, and does not lean. At a click it fades in half a second and the light on the pill fades out, with no snuffer or smoke; at the second click it fades back, with no match. The words change with a fade; the pill does not swell and the words do not widen.

Pitfalls

  • The smoke drawn one segment at a time with round ends looked like a string of beads: where two transparent segments overlap the grey doubles. The ribbon is now drawn in runs of four particles, each a single path with flat ends.
  • A click during the smoke stopped the putting-out timeline halfway, and the wick was left with its emission on: it smoked forever even with the flame lit again, and the loop never slept. When the wick catches, the match's timeline brings the smoke and the ember to zero.
  • The match came in from the edge of the canvas, cut sharp. It now fades in as it enters, and the smoke fades towards the edges.
  • The flame's bloom made with a radial gradient was a glowing dot. It is the flame's own outline, larger and blurred with shadowBlur; the light on the pill is a wide ellipse that warms it, not a spot.

Libraries

GSAP 3.15.0, Mona Sans (font variabile) 5.3.0.

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

Testing

Tested on 12 October 2026

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

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