Skip to content
animorio
IT
All components

The banner on the rail

Loading, Physics, 3DGSAP, CanvasAdvancedCompressed weight: Demo 11 KBGSAP 28 KBFreeDownload the zip

A fabric banner being uploaded to an online printer: the banner hangs from rings on a rail, gathered at the left in pleats like a curtain pulled aside, and as the file goes up the rings slide along the rail and the fabric opens pleat by pleat until the print hangs flat. It is a real cloth in three dimensions, points and sticks with gravity, and every triangle is drawn with the print on it and lit by its normal, so the pleats have their shade and the back shows inside them. If the connection drops the last ring lets go and the corner falls until Retry hooks it again; at the end a breath of wind crosses the banner, and Upload another gathers it again.

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 banner on the rail</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/instrument-sans@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the banner on the rail: a print shop uploading a fabric banner, and the banner hangs from rings on a rail,
         gathered at the left in pleats. As the file goes up the rings slide out along the rail and the fabric opens,
         pleat by pleat, until the whole print hangs flat. The cloth is a real cloth in 3D (points, sticks, gravity),
         printed and lit triangle by triangle on the canvas by script.js -->
    <section class="bn" aria-labelledby="bn-title" data-banner>
      <div class="bn__head">
        <div class="bn__words">
          <h2 class="bn__title" id="bn-title"><span class="bn__swap" data-title>Fabric banner</span></h2>
          <p class="bn__meta"><span class="bn__swap" data-meta>night-market.pdf · 48 MB</span></p>
        </div>
        <p class="bn__figure" aria-hidden="true">
          <span class="bn__pct" data-pct>0%</span>
          <svg class="bn__tick" viewBox="0 0 28 28" data-tick><path d="M6.5 14.8l5 5 10-11.4" pathLength="1"/></svg>
        </p>
      </div>

      <div class="bn__rail" role="progressbar" aria-labelledby="bn-title" aria-valuemin="0" aria-valuemax="100"
           aria-valuenow="0" aria-valuetext="Not started" data-bar>
        <canvas class="bn__canvas" aria-hidden="true" data-cloth></canvas>
      </div>

      <div class="bn__foot">
        <span class="bn__status"><span class="bn__swap" data-status>200 × 110 cm · polyester</span></span>
        <button class="pill" type="button" data-btn data-autoclic>
          <span class="pill__fill" aria-hidden="true"></span>
          <span class="pill__label" data-label>Upload</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.

35 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 banner on the rail” from Animorio and fit it into my site

What it does

On a black background, alone in the middle: at the top Fabric banner and under it night-market.pdf · 48 MB, with 0% on the right; below, a thin grey rail with a knob at each end, and hanging from nine rings a cream banner gathered all to the left in tight pleats, like a curtain pulled aside: between the pleats bits of the print show, blue letters and an orange sun. At the foot 200 × 110 cm · polyester and the light pill Upload. Coming in, the rings arrive a little open and spring back, and the pleats sway and settle. Press Upload and the rings slide along the rail on springs, all spreading together, and the fabric opens: the pleats open one after another, each with a lit face and a shaded one, the back darker inside the fold, and the print reads a little at a time, NIGHT MARKET, the sun, Fridays · Pier 9 · 7 pm. Below, the megabytes go up, 22.4 of 48 MB, and the percentage with them; the pill becomes a ring that says Pause, which stops the rings, and Resume. One time in two the connection drops: the last ring turns red and lets go of the fabric, the right corner falls and swings, the title says Connection lost in red and under it 30.0 MB kept; Retry hooks the corner again, which rises to its ring, and the rings carry on. At the end the banner hangs flat with small scallops between the rings, a breath of wind crosses it from left to right swelling it a little, the percentage gives way to a green tick, the title says Ready for print and under it We will email you a proof. Upload another sends the rings back to the left and the fabric gathers into its pleats again.

How it works

A cloth of points and sticks

The banner is a grid of 17 by 11 points in three dimensions, joined by sticks along the rows and columns, on the diagonals (softer) and, downwards, two points apart, so it hangs like fabric and not like a chain. Every step (1/120 of a second, at most six a frame) moves the points with Verlet, with gravity and a little damping, then seven times puts every stick back to its length. There is no stick two points apart across: it would push the pleats open and the banner would fan out at its hem.

The rings and the pleats

A ring on every other column holds the top point under it; the rings head for their places on a spring, and the place depends on the progress: GATHER apart when the banner is gathered, an eighth of its width when it is open. Squeezed between two rings, the column in between has to go forward or back: the starting pose is worked out by hand, every other column forward or back by just enough to keep the sticks their length, and a light pull towards that pose (the memory of the pleats, stronger while it gathers) makes it fold like an accordion and not into a heap.

The print on every triangle

The print is a separate canvas, drawn at twice its size. Every square of the grid is two triangles, projected in perspective and sorted from the farthest to the nearest; for every triangle the script works out the affine transform that takes its three points on the print to its three points on the screen, clips the triangle, grown by almost a pixel away from its middle so no line is left between neighbours, and draws the whole print through that transform.

The light of the pleats

The normal of every triangle, turned to face the camera, gives its light, which comes from the upper left and in front. The shade is measured against the flat banner: a flat triangle has no shade, so no seam shows on the open banner, and only the faces turned away darken, those turned to the light brighten a little. The winding of its points on the screen says whether the front or the back is seen, and the back, inside a pleat, is plainer and darker.

The lost connection and the wind

When the connection drops the last ring lets go of its point, which falls freely; Retry hooks it again by pulling it a little towards the ring at every step until it is held again. At the end the wind is a bell-shaped push backwards that runs along the banner from left to right in a little over a second, stronger towards the hem. When the rings are still and the points no longer move, the loop leaves GSAP’s ticker and draws nothing.

Values to change

NameFileDefaultWhat it changes
SIZE_MBscript.js48How big the file is, in megabytes.
SECONDSscript.js6How many seconds the upload takes in the demo.
RINGSscript.js9How many rings hold the banner.
GATHERscript.js6How close the rings sit with the banner gathered, in pixels.
STIFFscript.js0.5How stiff the fabric hangs, from 0 (silk) to 1 (canvas).
GRAVITYscript.js1400How hard gravity pulls, in pixels per second squared.
WINDscript.js1How strong the breath of wind at the end is (0 none).
WORDSscript.js['NIGHT', 'MARKET', 'Fridays · Pier 9 · 7 pm']The banner’s print: two big lines and a small one.
FAIL_EVERYscript.js2One upload in how many loses the connection (0 never).
FAILscript.js0.62Where the connection is lost, from 0 to 1.
PREVIEWscript.js1.4In the preview’s round: how many times faster.
LABELSscript.js['Upload', 'Pause', 'Resume', 'Retry', 'Upload another']The words of the pill, state by state.
--fabricstyle.css#efe8daThe colour of the fabric.
--print-sunstyle.css#ff5a36The sun on the print.

Try them in the Playground

When to use it

For uploading a file to print at an online printer (banners, posters, curtains, T-shirts), for sending a design or an artwork in a creative app, for any upload where what goes up is something to hang and look at.

On phones

The panel takes the whole width less the margins and the scene shrinks with it, because it is drawn in units of its box: the cloth and its movements are the same. The pill answers a touch as it does the mouse.

With reduced motion

No physics: the banner simply takes the pose worked out for the progress, gathered, half open or flat, without swinging, with no springs in the rings and no wind; the corner that lets go folds down instead of falling. The words change without moving.

Pitfalls

  • With sticks two points apart across as well, the pleats pushed themselves open and the gathered banner fanned out at its hem, right out of the panel: now they only run downwards.
  • Filling the shade on every triangle, the flat banner too had a dark line on every diagonal, where the grown triangles overlap: now the shade is measured against the flat banner, which has none, with a small dead zone.
  • The triangles were grown by moving every point half a pixel in x and y by sign: on the diagonal of each square the two points moved along the diagonal itself and the line stayed. Now every point moves away from the middle of its triangle.
  • Going back to rest the rings squeezed the fabric too fast and it crumpled into a crooked heap: now a light pull towards the pleated pose guides it, stronger while it gathers and off while the wind blows or a corner hangs loose.

Libraries

GSAP 3.15.0, Instrument Sans (font variabile, assi wght e wdth) 5.3.0.

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

Testing

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