
The cookie banner
Consent, Transitions, SVG, Physics
NewFree
The button that sends a campaign: a 3, 2, 1 countdown on a rolling digit, the rocket shaking in its well, ignition with sparks and smoke pouring out from under the pill, then liftoff with a plume that curls in the air until the rocket is gone off the top. It stays Launched with a thin wisp of smoke, and a new rocket rises out of the well.
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 rocket launch button</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/geist@5.3.0/index.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- the smoke under the button: the exhaust pours out from under the pill as from a launch pad, and the plume -->
<canvas class="sky sky--back" data-sky="back" aria-hidden="true"></canvas>
<main class="stage">
<!-- the launch button: a countdown on a rolling digit, the rocket shakes on its pad, lifts off with a plume of
smoke and flies out of the page; the button stays Launched with a thin wisp of smoke, then a new rocket rises -->
<div class="launch" data-launch data-autoclic>
<button class="launch__btn" type="button" aria-label="Launch campaign to 12,480 subscribers" data-btn>
<span class="launch__heat" data-heat aria-hidden="true"></span>
<span class="launch__pad" data-pad aria-hidden="true">
<svg class="launch__ring" viewBox="0 0 64 64"><circle cx="32" cy="32" r="30" pathLength="1" data-ring/></svg>
</span>
<span class="launch__label" aria-hidden="true">
<span class="launch__line" data-line="idle">Launch campaign</span>
<span class="launch__line" data-line="count">Launching in<span class="launch__digits"><span class="launch__reel" data-reel></span></span></span>
<span class="launch__line launch__line--done" data-line="done"><svg class="launch__check" viewBox="0 0 24 24"><path d="M5 12.6l4.3 4.3L19 7.2" pathLength="1" data-check/></svg>Launched</span>
</span>
</button>
<p class="launch__hint"><span data-hint>12,480 subscribers, sent all at once</span></p>
<p class="sr" aria-live="polite" data-live></p>
</div>
</main>
<!-- the smoke over the button: the vapour before ignition, the puffs at the rim of the well, the thin wisp -->
<canvas class="sky" data-sky="front" aria-hidden="true"></canvas>
<!-- the rocket: it sits over the pad, and the clip lets a new one rise out of the pad as from a silo -->
<svg class="rocket" data-rocket viewBox="-40 -60 80 120" aria-hidden="true">
<defs>
<linearGradient id="rk-hull" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" class="rocket__hull-a"/>
<stop offset="0.38" class="rocket__hull-b"/>
<stop offset="0.72" class="rocket__hull-c"/>
<stop offset="1" class="rocket__hull-d"/>
</linearGradient>
<linearGradient id="rk-fin" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" class="rocket__fin-a"/>
<stop offset="1" class="rocket__fin-b"/>
</linearGradient>
<linearGradient id="rk-metal" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#5b6070"/>
<stop offset="0.45" stop-color="#b9bdc8"/>
<stop offset="1" stop-color="#3d414d"/>
</linearGradient>
<linearGradient id="rk-heat" x1="0" y1="0" x2="0" y2="1">
<stop offset="0.35" class="rocket__heat-a"/>
<stop offset="1" class="rocket__heat-b"/>
</linearGradient>
<radialGradient id="rk-glow">
<stop offset="0" class="rocket__glow-a"/>
<stop offset="0.35" class="rocket__glow-m"/>
<stop offset="1" class="rocket__glow-b"/>
</radialGradient>
<linearGradient id="rk-flame" x1="0" y1="0" x2="0" y2="1" gradientUnits="objectBoundingBox">
<stop offset="0" class="rocket__flame-a"/>
<stop offset="0.3" class="rocket__flame-b"/>
<stop offset="1" class="rocket__flame-c"/>
</linearGradient>
<linearGradient id="rk-core" x1="0" y1="0" x2="0" y2="1" gradientUnits="objectBoundingBox">
<stop offset="0" stop-color="#fff"/>
<stop offset="0.5" class="rocket__core-a"/>
<stop offset="1" class="rocket__core-b"/>
</linearGradient>
<clipPath id="rk-body"><path d="M0 -27C7.4 -21 10.2 -10 10.2 3.5L9.2 15.2Q9 17.4 6.9 17.4H-6.9Q-9 17.4 -9.2 15.2L-10.2 3.5C-10.2 -10 -7.4 -21 0 -27Z"/></clipPath>
<clipPath id="rk-silo" clipPathUnits="userSpaceOnUse"><path data-silo d="M-600 -6000H600V0H26A26 26 0 0 1 -26 0H-600Z"/></clipPath>
</defs>
<!-- the light of the flame round the rocket, and where the flame hits the floor of the well -->
<circle class="rocket__halo" r="1" data-halo/>
<ellipse class="rocket__splash" cx="0" cy="25" rx="40" ry="8" data-splash/>
<g clip-path="url(#rk-silo)">
<g data-craft>
<!-- the flame, redrawn every frame under the nozzle: the outer flame, the white core, the bright knots -->
<g class="rocket__flame" data-flame>
<path class="rocket__fire" data-fire/>
<path class="rocket__core" data-core/>
<path class="rocket__knots" data-knots/>
</g>
<path class="rocket__fin" d="M-9.4 1.5C-14.8 5.6 -17.6 11.6 -17.4 20.6L-9 15.6Z"/>
<path class="rocket__fin" d="M9.4 1.5C14.8 5.6 17.6 11.6 17.4 20.6L9 15.6Z"/>
<path class="rocket__nozzle" d="M-5.2 17.2H5.2L6.8 23.4Q6.9 24.2 6.1 24.2H-6.1Q-6.9 24.2 -6.8 23.4Z"/>
<path class="rocket__hull" d="M0 -27C7.4 -21 10.2 -10 10.2 3.5L9.2 15.2Q9 17.4 6.9 17.4H-6.9Q-9 17.4 -9.2 15.2L-10.2 3.5C-10.2 -10 -7.4 -21 0 -27Z"/>
<g clip-path="url(#rk-body)">
<rect class="rocket__cap" x="-12" y="-30" width="24" height="15.5"/>
<rect class="rocket__band" x="-12" y="8.2" width="24" height="2"/>
<rect class="rocket__burn" x="-12" y="-4" width="24" height="24" data-burn/>
</g>
<path class="rocket__keel" d="M-1.3 9.5H1.3V22.2Q1.3 23.4 0 23.4Q-1.3 23.4 -1.3 22.2Z"/>
<circle class="rocket__rim" cx="0" cy="-4" r="5.2"/>
<circle class="rocket__glass" cx="0" cy="-4" r="3.6"/>
<path class="rocket__shine" d="M-2.2 -5.6A2.6 2.6 0 0 1 0.2 -6.9"/>
</g>
</g>
<!-- the sparks of the ignition: short streaks, all in one path -->
<path class="rocket__sparks" data-sparks/>
</svg>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="script.js"></script>
</body>
</html>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.
23 values to change
With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:
Take “The rocket launch button” from Animorio and fit it into my siteThe digits from COUNT to 1 sit in a column inside a window one digit high: at every beat (BEAT seconds) the column moves up one digit with expo.out, like a mechanical counter. Meanwhile an SVG circle with pathLength 1 fills with stroke-dashoffset from 1 to 0, and a shake number that GSAP grows with power1.in sets how hard the rocket shakes.
The rocket is an SVG fixed on the page, outside the button, so it can fly away: every frame it puts itself over the well by reading getBoundingClientRect, and follows the button when it shrinks, trembles or the window changes size. The shaking is simplex noise read at high speed, the lean grows with the square of the climb, and a clipPath shaped like the well lets the new rocket rise from its bottom as from a silo.
The flame is drawn in the rocket's own frame, under the nozzle: two Bézier curves that meet at the tip, an orange one outside and a white one inside. Its length follows the thrust (from pilot flame to full power) and flickers with two noises at different speeds; on the pad it is cut at the floor of the well and spreads there into a flattened glow. At full thrust the bright knots of the exhaust appear.
Each puff is a particle on a canvas as big as the page: it is pushed by the curl of a noise field that drifts in time (the noise's slope turned by 90 degrees), so the smoke swirls on itself without ever bunching up; it rises a little, is slowed by the air, grows and fades as it ages. On the pad the exhaust goes down, hits the floor under the button and spreads to both sides; in flight the rocket leaves a puff every seven pixels of its way.
The smoke is on two canvases: the one under the button takes the exhaust and the first stretch of the plume, which so pours out from under the pill without covering the words; the one over it takes the vapour, the puffs at the rim of the well and the last thin wisp. The puffs are four ready-made drawings of heaped little clouds, in two versions, grey and warmed by the flame, and the canvas runs at half resolution: smoke is soft, and the graphics card does a quarter of the work.
| Name | File | Default | What it changes |
|---|---|---|---|
COUNT | script.js | 3 | The number the countdown starts from. |
BEAT | script.js | 0.8 | The seconds each number of the countdown lasts. |
SHAKE | script.js | 1 | How hard the rocket shakes on the pad before it lifts off. |
FLIGHT | script.js | 1.5 | The seconds the rocket takes to fly out of the page. |
LEAN | script.js | 9 | How many degrees the rocket leans to the right as it climbs. |
SMOKE | script.js | 1 | How much smoke: 0.5 is half the puffs, 2 twice as many. |
SWIRL | script.js | 1 | How much the smoke curls and swirls as it rises. |
DONE | script.js | 2.6 | The seconds the button says Launched before a new rocket rises. |
For an app's big action, the one that goes and does not come back: sending a campaign or a newsletter, publishing a site, deploying, launching a product. The countdown leaves time to think again and abort. It wants a dark page and room above the button, where the rocket flies.
The button is 340 pixels wide, or the screen minus 48; the rocket puts itself back over the well every frame, so it also follows the screen turning. A tap does the countdown like a click; the pilot flame of the mouse passing over does not come with a touch. The smoke canvases run at half resolution on dense screens too.
The countdown stays, with the digits changing without rolling and the ring filling; the rocket does not shake or fly: at zero it fades out of the well and Launched appears with the tick. No smoke, flame or sparks. On the way back the rocket reappears with a fade.
GSAP 3.15.0, Geist (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 13 KB; the libraries: GSAP 28 KB.
Tested on 4 October 2026
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.
With your account’s email and password.
or with your email
You’re signed in as .
With your account, you can download the zip of every animation and every pack, for free.