Skip to content
animorio
IT
All components

The comparing column chart

Charts, Hover, SVG, PhysicsGSAPAdvancedCompressed weight: Demo 12 KBGSAP 28 KBFreeDownload the zip

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.

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 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>

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.

51 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 comparing column chart” from Animorio and fit it into my site

What it does

On a black background, top left Tallis Bindery · Sales, the total large and light (€30,650) and under it Jan–Sep 2026 · +6% on 2025; on the right the choice of year, 2025 / 2026, with a thin lever between them leaning towards the year chosen, and under it the key: a small solid column for 2026 and an outline for 2025. Below, twelve months on a baseline, with four barely visible guides (€2k, €4k, €6k, €8k). Every month has two columns one inside the other: 2026's, solid, warm white and three pixels wide, and 2025's, a one-pixel grey outline nine wide, open at the bottom. So you see at once whether a month went over the year before's mark or stayed under it; October, November and December only have the outline, because 2026 is not over yet. On arrival the baseline draws itself from the left, the outlines rise and then the solid columns grow, one after the other, going a pixel too far and settling. Pointing at a month the other columns go dark and step aside on springs, the nearest first, to make room beside it: there an electric blue dimension, as in a technical drawing, measures the difference between the two tops. The extension lines start from the outline, the dimension line grows from 2025's top to 2026's with an oblique tick at each end, and beside the growing tip the difference counts to its value (+12%). The month's name underneath turns black, wider and heavier, and at the top the figure rolls into the month's sales and takes weight (€3,380, March 2026 · +12% on 2025). Choosing 2025, the 2026 columns sink into the baseline, the 2025 outlines close into solid columns and the 2024 outlines rise from below; the lever leans the other way and the total turns to €45,170, All of 2025 · +9% on 2024. Going back to 2026 the opposite happens: the solid columns open into outlines, their two halves parting and thinning, and the new ones grow inside.

How it works

A column that becomes an outline

Every 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.

Staggered springs, one pixel too far

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.

The columns that make room

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 technical-drawing dimension

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.

The type that takes weight

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.

Keys, fingers and the preview

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.

Values to change

NameFileDefaultWhat it changes
THIS_YEARscript.js[2840, 3390, 3380, …]This year's sales month by month, from January: the months still to come are left out.
LAST_YEARscript.js[2610, 3140, 3020, …]Last year's sales, all twelve months.
YEAR_BEFOREscript.js[2380, 2950, 2710, …]The year before, to compare last year with.
YEARscript.js2026The year of THIS_YEAR.
CURRENCYscript.js'€'The sign before the figures.
GROWscript.js[120, 0.86]The columns' spring: how stiff, and how much it is held back (1 = no overshoot).
STAGGERscript.js0.05The seconds between one column and the next.
SLIDEscript.js[240, 0.8]The spring the columns step aside on, to make room for the dimension.
RIPPLEscript.js0.02How much later, month by month, the columns further away step aside.
MEASUREscript.js0.65The seconds the dimension takes to measure the difference.
HEAVYscript.js640The weight the month's figure takes (the total is at 330).

Try them in the Playground

When to use it

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.

On phones

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.

With reduced motion

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.

Pitfalls

  • A one-pixel outline at a fractional position turns into a blurred two-pixel grey line: at rest every column's centre goes to a half pixel and the heights to whole pixels, so columns and outlines are crisp; not while moving, or they would jump.
  • Looking for the month under the pointer in the current positions made the choice flicker: the month that widens moves the boundary and the pointer ends up in the neighbour. It is looked up in the places the columns are going to.
  • On its spring, a sinking column went past zero and bounced back out of the baseline: below zero it stops.
  • The difference counting from zero started at +0% even for a month that went down and changed sign halfway: the sign is taken from the final value.
  • The month names have their position written by the script every frame: an entrance with GSAP's y wiped it out. They only fade in.
  • Heights and places in pixels were lost on a change of size in the middle of a movement: the heights are kept in euros and the places in parts of the width, and on resize only the drawing changes.

Libraries

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.

Testing

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