Skip to content
animorio
IT
All components

The filling honeycomb

Loading, Physics, TransitionsCanvas, GSAPAdvancedCompressed weight: Demo 14 KBGSAP 28 KBFreeDownload the zip

An import of 2,400 contacts made as a piece of wax honeycomb: every cell is a batch of contacts. A thread of honey runs down into the cell and the honey rises with its meniscus climbing the walls, pale where it is thin and dark where it is thick; when the batch is in a wax cap closes the cell from the walls to the middle and swells with a little bounce. The cells fill four at a time in a spiral from the middle. If a batch cannot be read its cell stays empty and the wax cracks; at the end a warm light runs across the sealed comb: Import complete.

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, viewport-fit=cover">
  <title>The filling honeycomb</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/standard.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the filling honeycomb: a contacts import shown as a piece of wax comb. Every cell is a batch of contacts: it fills
         with honey that rises with its meniscus, glowing where the light comes through, and when it is full a wax cap
         closes over it from the walls inwards and swells a little. The cells fill in a spiral from the middle. The comb
         is painted by script.js on the canvas, one cell at a time -->
    <section class="favo" aria-label="Contacts import" data-favo>
      <div class="favo__comb" role="progressbar" aria-label="Import progress" aria-valuemin="0" aria-valuemax="100"
           aria-valuenow="0" aria-valuetext="Ready to import" data-comb>
        <canvas class="favo__paint" aria-hidden="true" data-paint></canvas>
      </div>
      <div class="favo__info" aria-hidden="true">
        <p class="favo__title">
          <svg class="favo__tick" viewBox="0 0 24 24" data-tick><path d="M5.5 12.6l4.2 4.2 8.8-9.4" pathLength="1"/></svg>
          <span class="favo__swap" data-title>contacts.csv</span>
        </p>
        <p class="favo__pct" data-pct>0%</p>
        <p class="favo__meta"><span class="favo__swap" data-meta>2,400 contacts · 418 KB</span></p>
      </div>
      <div class="favo__buttons">
        <button class="pill" type="button" data-go data-autoclic><span class="pill__label" data-label>Import contacts</span></button>
      </div>
      <p class="sr" role="status" aria-live="polite" data-live></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.

39 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 filling honeycomb” from Animorio and fit it into my site

What it does

On a black background, alone in the middle, a piece of golden wax honeycomb 380 pixels wide: about fifty empty, dark hexagonal cells, with a little light coming through their thin wax bottoms, and round them the half built cells fading into the dark. The cells’ corners are nudged a little, as in a real comb. Underneath are contacts.csv, 2,400 contacts · 418 KB and the Import contacts pill. Pointing at the pill gathers a bead of honey in the middle cell, ready to run down. Pressed, the import starts: the line says Importing contacts, under it the contacts that have arrived (1,282 of 2,400 contacts), on the right the big percentage, and the pill becomes an outlined Cancel. Every cell is a batch of about 47 contacts, and four batches travel at once: a thread of honey runs down into each cell, swaying a little, and the honey rises from the bottom with a small mound where the thread touches it and the meniscus climbing both walls, with a line of light on the surface. The honey is pale near the surface and turns dark amber at the bottom, darker still along the walls, and the light coming through gathers low in the middle as in a lens. When the batch is in the thread thins and its tail falls into the cell; once the cell is full a wax cap closes it from the walls to the middle, like an iris, and at the end swells with a little bounce and a flash of light on the dome. The cells fill in a spiral from the middle, so the sealed comb grows the way bees would build it. In the demo one import in two stops halfway: in one cell the thread runs down but no honey gathers, the thread breaks and the wax bottom cracks in four red lines while the cell shudders; the batches already travelling finish, the line becomes Import paused in red with Rows 1,085–1,131 couldn’t be read, and the pill says Retry, which closes the cracks and fills the cell. At the end the whole comb is sealed, a warm light crosses it on a slant and the line becomes Import complete with a tick that draws itself and 2,400 added · 37 duplicates merged. Import again melts the caps and drains the honey from the edge to the middle and starts over; Cancel empties the comb and goes back to rest.

How it works

The hexagonal grid, made by hand

The cells are pointy topped hexagons on axial coordinates: x = R·√3·(q + r/2), y = 1.5·R·r. The ones whose centre lies inside an ellipse one radius smaller than the comb are batches; those between the two ellipses are half built cells, darkened towards black. Every corner of the grid is nudged by an amount taken from a hash of its rounded position, so all the cells that share it move it the same way and the comb stays closed but not perfect. The wall is the inner hexagon, shrunk towards the centre by WALL/2 ÷ cos 30°.

The empty comb is painted once

Wax, bottoms, the shade of the top walls and the light on the bottom ones go on a canvas of their own, at the start and at every resize. On every frame only the cells that changed are painted again: each one is clipped to its own hexagon, the same piece of the empty comb is copied over it with drawImage, and honey, thread, cap and cracks are drawn. Cells never overlap, so no cell dirties its neighbours. Only the final light paints everything, for just over a second; when nothing moves nothing is drawn.

Honey with its meniscus and its light

The surface is a function of x: the level, less the meniscus rising near the walls with two exponentials (the walls are found by crossing the horizontal with the inner polygon), less a gaussian mound where the thread lands. The polygon is filled with a vertical gradient that starts at the surface and always has the same scale, so a cell just begun is pale and a full one reaches dark amber: more honey, less light. Over it go a dark radial gradient towards the walls, a light one in screen mode low in the middle and two lines of light under the surface.

The batches and the thickness

The cells are sorted by hexagonal ring plus the angle round the centre (a spiral) or, with ORDER = 'wave', by a slanted line plus a little noise. WORKERS batches travel together, each with its own duration and sometimes a stall. The level follows the data with an exponential delay of THICK seconds, but with a minimum speed, otherwise the last per cent would never arrive. Once the cell is full, a GSAP timeline closes the cap like an iris (a hexagon with a hexagonal hole, filled evenodd), then swells it by 5% and lets it settle with elastic.out.

Failure, the end and those who cannot see

The batch that goes wrong runs its thread for 0.7 seconds without honey, then stops: four zigzag cracks worked out from a hash grow from the middle of the cell while it shudders, and the batches in flight finish by themselves. Retry closes the cracks and puts the batch back at the head of the queue. The comb is a role=progressbar with aria-valuenow and aria-valuetext updated at most every half second, aria-busy while it works and an aria-live line for the start, the failure, the end and a cancel. Clicks from a script (the library preview’s) never cancel and start again only after 2.4 seconds of Import complete.

Values to change

NameFileDefaultWhat it changes
SECONDSscript.js7How long the demo import takes, in seconds (a real one follows its batches).
CONTACTSscript.js2400The contacts to import: they are shared out among the cells.
CELLscript.js19A cell’s radius, in units of the 380 wide drawing: smaller cells, more batches. From 14 to 26.
WORKERSscript.js4How many batches are imported together, that is how many cells fill at a time.
ORDERscript.js'spiral'The order of the cells: 'spiral' in a spiral from the middle, 'wave' as a front from the left.
FAIL_EVERYscript.js2In the demo, one import in how many goes wrong (0 never).
FAIL_ATscript.js0.45Where in the import the batch that cannot be read is, from 0 to 1.
THICKscript.js0.22How thick the honey is: the seconds the level takes to catch up with the data.
CAPscript.js0.34How long the wax cap takes to close a full cell, in seconds.
WORDSscript.js{ busy: 'Importing contacts', error: 'Import paused', done: 'Import complete' }The words under the comb while importing, on failure and at the end; the one at rest is in index.html.
LABELSscript.js{ busy: 'Cancel', error: 'Retry', done: 'Import again' }The button’s words at the same three moments; the one at rest is in index.html.

Try them in the Playground

When to use it

For importing contacts into a CRM or a newsletter, loading a CSV into a sheet or a database, syncing an address book, indexing documents or any job split into parallel batches where it is worth seeing which batches are done and which one stopped. The comb also suits a product with a warm, crafted tone.

On phones

The comb is 380 pixels wide and on a phone takes the width of the screen less 16 pixels a side (358 at 390): the drawing scales with the width and the cells stay the same. The pill is 44 tall and works by touch; the bead that gathers when the pill is pointed at and the swelling pill are only there with a mouse or a keyboard.

With reduced motion

No thread, no rising honey, no iris and no bounce: the honey in each cell fades in as its batch arrives, then the cap fades in over it. The cracks fade in whole, without shuddering, and at the end there is no light crossing the comb: the tick appears. The words change with a fade.

Pitfalls

  • Painting a cell again, its hexagon was first wiped with clearRect: a half transparent pixel stayed on the antialiased edge and a dark seam appeared round the cell. The empty comb is opaque inside every hexagon, so copying it over without wiping is enough.
  • The glint on the cap’s dome was drawn before the cell’s outline: the following stroke() went over the ellipse just made and a grey ring appeared on every cell. The glint now comes last.
  • Corners nudged at random pulled the cells apart: every corner has to be nudged by a hash of its rounded position, not of the cell, so neighbouring cells move it the same way.
  • Opaque cream caps turned the finished comb into a floor of pale tiles: now the wax is a little see-through over the honey and every cell has a slightly different tint.
  • The failed batch stayed in the list of those in flight and kept the drawing loop awake for ever: on failure it leaves the list and goes back in with Retry, at the head.
  • Mona Sans’s tabular figures have a seriffed 1 and look typewritten: the percentage and the count use the normal figures.

Libraries

GSAP 3.15.0, Mona Sans (font variabile) 5.3.0.

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

Testing

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