
The water button
Buttons, SVG, Physics, Hover
NewFree
A small bindery's sales month by month in columns of wire, in the minimal style: this year's are thin and solid, last year's stand behind them as open outlines. Changing year the solid columns open into outlines and the new ones grow inside, staggered on springs; pointing at a month the columns step aside to make room and a technical-drawing dimension measures the difference between the two tops, while the month's figure takes weight.
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 comparing column chart</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- a small bindery's sales, month by month, in columns of wire: this year's columns are solid hairlines three
pixels wide, last year's stand behind them as thin open outlines. Choosing the other year, the solid columns
open into outlines and the new ones grow inside them; pointing at a month, the columns step aside to make
room and a dimension measures the difference between the two tops. The chart is drawn by the script -->
<section class="chart" aria-labelledby="chart-title" data-chart data-autoclic>
<header class="chart__head">
<div class="read">
<p class="read__brand" id="chart-title">Tallis Bindery · Sales</p>
<p class="read__figure" data-figure></p>
<p class="read__note" data-note></p>
</div>
<div class="side">
<div class="years" role="radiogroup" aria-label="Year" data-years></div>
<p class="legend" aria-hidden="true">
<span class="legend__item">
<svg class="legend__mark" viewBox="0 0 9 12" width="9" height="12"><rect x="3" y="0" width="3" height="12"/></svg>
<span class="legend__year" data-legend-now></span>
</span>
<span class="legend__item legend__item--then">
<svg class="legend__mark" viewBox="0 0 9 12" width="9" height="12"><path d="M0 12V0h9v12H8V1H1v11z"/></svg>
<span class="legend__year" data-legend-then></span>
</span>
</p>
</div>
</header>
<div class="plot" tabindex="0" role="group" data-plot>
<svg class="plot__svg" aria-hidden="true" data-svg>
<g class="plot__guides" data-guides></g>
<line class="plot__base" data-base/>
<g data-columns></g>
<g data-quotas></g>
</svg>
<div class="plot__marks" aria-hidden="true" data-quota-labels></div>
<div class="months" aria-hidden="true" data-months></div>
</div>
<p class="sr" aria-live="polite" data-said></p>
</section>
</main>
<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.
51 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 comparing column chart” from Animorio and fit it into my siteEvery year and month is an object with a height (in euros) and an opening from 0 to 1, drawn as a single SVG path made of three rectangles: two sides and a one-pixel cap. At opening 0 the two sides are a pixel and a half wide and touch, so it is a solid column three pixels wide; at opening 1 they are one pixel wide and stand at the edges of an outline nine wide (seven on a phone). In between they part, thin out and the colour turns from warm white to grey: so this year's column becomes the year before's outline.
The heights are damped springs (GROW, damping 0.86: for a 200 pixel column the overshoot is about one pixel) worked out in steps of 1/240 s on GSAP's ticker, with the step held to 1/30 s. Changing year every column gets its new goal STAGGER seconds later per month: first the ones leaving sink (stopped at zero, without bouncing back out of the baseline), then the outlines open or close, then the new ones grow.
Pointing at a month, its slot widens as much as the dimension needs (the width of the figure is measured once and remembered) and the other eleven give up the same share each, so the row stays the same length. Every month goes to its place on its own spring (SLIDE), with a delay that grows with the distance (RIPPLE): the neighbours first, then the far ones. The month under the pointer is looked up in the places the columns are going to, not where they are, so moving from month to month never flickers.
The dimension is a path drawn again every frame from the real heights, so it follows the columns even while they grow: the extension lines start two pixels from the outline and run four past the dimension line, the dimension line grows from the old top to the new one in MEASURE seconds with oblique ticks at its ends, and the figure beside the tip counts with the right sign from the start. There are two dimensions, so the one going out fades while the new one measures.
Archivo is a variable typeface with a weight axis (100-900) and a width axis (62-125): GSAP runs the CSS variables --w and --wd. The chosen month goes to weight 700 and width 125 with back.out, the figure at the top rolls into the month's value and goes from 330 to HEAVY, and the chosen year widens and fills; the digits are tabular, so they never wobble.
The chart is reached with Tab and read with the arrows, Home, End and Esc; the years are a radiogroup with the arrows, and a line for screen readers says month, sales, difference and the year before's value. A finger taps a month or slides sideways across the chart (the page still scrolls up and down), and tapping the same month again, or outside, goes back to the total. A click from a script (the library's preview) plays a twelve-second round in a GSAP timeline: three months, 2025, a month of 2025, 2026 again and rest.
| Name | File | Default | What it changes |
|---|---|---|---|
THIS_YEAR | script.js | [2840, 3390, 3380, …] | This year's sales month by month, from January: the months still to come are left out. |
LAST_YEAR | script.js | [2610, 3140, 3020, …] | Last year's sales, all twelve months. |
YEAR_BEFORE | script.js | [2380, 2950, 2710, …] | The year before, to compare last year with. |
YEAR | script.js | 2026 | The year of THIS_YEAR. |
CURRENCY | script.js | '€' | The sign before the figures. |
GROW | script.js | [120, 0.86] | The columns' spring: how stiff, and how much it is held back (1 = no overshoot). |
STAGGER | script.js | 0.05 | The seconds between one column and the next. |
SLIDE | script.js | [240, 0.8] | The spring the columns step aside on, to make room for the dimension. |
RIPPLE | script.js | 0.02 | How much later, month by month, the columns further away step aside. |
MEASURE | script.js | 0.65 | The seconds the dimension takes to measure the difference. |
HEAVY | script.js | 640 | The weight the month's figure takes (the total is at 330). |
For a shop's or a workshop's dashboard, the end-of-month summary, a place's bookings year on year, a project's results: wherever it matters whether this month did better or worse than the same month a year ago.
The chart takes the width of the screen, the months become initials and the outline narrows to seven pixels; the dimension and the room opening for it are the same. A finger taps a month or slides sideways across the chart, and the page still scrolls up and down.
The columns are already in place and change at once when the year changes, with the chart fading back in; the columns do not step aside, the dimension fades in already drawn, the words swap with a fade and the weights change without a spring.
GSAP 3.15.0, Archivo (font variabile, assi wght e wdth) 5.3.0.
Compressed with gzip, the three demo files weigh 12 KB; the libraries: GSAP 28 KB.
Tested on 16 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.