Skip to content
animorio
IT
Back to the library

The hotel header with suitcases on the luggage cart

Physics, ScrollGSAP, CanvasAdvancedCompressed weight: Demo 13 KBGSAP + ScrollTrigger 45 KBLenis 5.3 KBMatter.js 25 KBFreeDownload the zip

Scroll inside the preview

The header of a made-up hotel, in the rich style: in a lobby with striped wallpaper, a checkered marble floor and a brass lift, as you scroll the guests’ bags fall one at a time onto the luggage cart with real physics, bounce, wedge in and pile up; the bell rings at every knock and the floor dial needle trembles. Scrolling back up empties the cart, and at the end the title and the button arrive.

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 suitcases on the hotel cart</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/full.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main>
    <!-- the header of a made-up hotel, the Larkspur: a lobby with striped wallpaper, a marble floor, the lift with its
         brass floor dial and a bellhop's brass luggage cart. Scrolling down, the guests' bags fall onto the cart one by
         one with real physics (Matter.js) and pile up; scrolling back up lifts them away again. Tap a bag to bump it -->
    <section class="carico" data-carico>
      <div class="palco" data-palco>
        <canvas class="scena" data-scena role="img" aria-label="A hotel lobby with a brass luggage cart. As you scroll, suitcases, a trunk, a hatbox and a duffel bag fall onto the cart and pile up."></canvas>

        <header class="testata">
          <p class="marchio"><svg viewBox="0 0 32 32" aria-hidden="true"><path d="M5 22h22M8 22a8 8 0 0 1 16 0M16 10v4M13 10h6"/></svg><span>Hotel Larkspur</span></p>
          <p class="sotto">Since 1931, by the station</p>
        </header>

        <p class="targa" data-targa aria-live="polite"><span class="targa__su">Now loading</span><span class="targa__nome" data-targa-nome>The lobby</span></p>

        <div class="finale" data-finale>
          <h1 class="finale__titolo">We carry <em>the rest.</em></h1>
          <p class="finale__testo">Leave your bags at the door. They’ll be in your room before you are.</p>
          <a class="tasto" href="#" data-tasto><span>Book a room</span></a>
        </div>

        <p class="cenno" data-cenno><span>Scroll to load the cart</span><svg viewBox="0 0 12 16" aria-hidden="true"><path d="M6 1v13M1 9l5 5 5-5"/></svg></p>
      </div>
    </section>

    <section class="dopo">
      <p class="dopo__marchio">Hotel Larkspur</p>
      <h2 class="dopo__titolo">Forty rooms, one brass cart.</h2>
      <p class="dopo__testo">Our porters meet every train. Your luggage goes up the old lift, you go up the stairs.</p>
    </section>
  </main>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/ScrollTrigger.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/lenis@1.3.26/dist/lenis.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/matter-js@0.20.0/build/matter.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 hotel header with suitcases on the luggage cart” from Animorio and fit it into my site

What it does

In the Hotel Larkspur’s lobby, with cream striped wallpaper and lily prints, a panelled burgundy skirting and a green checkered marble floor in perspective, the brass luggage cart waits empty on its red carpet, with shiny tubes, the curved bar on top and four casters. Next to it are the grille lift with its floor dial, a potted palm and a marble side table with a bell. As you scroll, the bags fall from above one at a time, and they are real bodies: the leather trunk with straps and brass corners, the ribbed teal hard-shell case with wheels and travel stickers, the red suitcase, the striped hatbox with its bow, the duffel bag with zip and handles, the vanity case with its mirror and the briefcase from room 214. They bounce, turn, wedge in and pile up between the uprights; the tags swing at every knock, a puff of dust rises, the bell rings and the dial needle trembles. The brass plate at the top says which bag is arriving. Scrolling back up the bags fly away in reverse order and the cart empties; at the bottom come We carry the rest. and the Book a room button. A tap or a click gives the top bag a little bump.

How it works

Scroll counts, physics falls

Scroll moves a number from 0 to 1 with scrub, and from it we know how many bags should be on the cart. Physics, though, runs in real time: if some are missing, a new one falls from above (at most one every 0.3 seconds); if there are too many, the last one becomes static and flies up. So scrub can never leave a bag halfway through its fall.

The cart is made of static bodies

Matter.js has the platform, the two uprights, the floor and two side walls, all static: the bags are chamfered rectangles and the hatbox a circle, with high friction so the pile stays up. The engine steps at a fixed 1/60 of a second accumulated in the GSAP ticker, the same on every screen.

Sizes come from the box

Everything is drawn in s units, the smaller of width over 640 and height over 780: the cart, the bags and the static bodies grow and shrink together, and the pile fits the phone box as it does the computer. On resize the static bodies are rebuilt and the bags fall again.

Knocks make the lobby ring

On collisionStart, if the impact speed passes a threshold, the bell gets a push and the floor needle a kick: both are damped springs computed every frame. The tags hanging from the bags are more springs tied to the body’s angular velocity.

The background is drawn once

Wallpaper, skirting, veined marble, lift and palm go on a spare canvas drawn only once (and again when the fonts arrive or the size changes); every frame copies it and draws the cart, the bags, the bell and the dial on top.

Values to change

NameFileDefaultWhat it changes
HOTELscript.js'Hotel Larkspur'The hotel’s name on the cart’s plate.
BAGSscript.js7How many bags fall onto the cart, from 3 to 7.
GRAVITYscript.js1.1Gravity: higher, the bags fall and settle faster.
BOUNCEscript.js0.18How much the bags bounce when they land.
SCREENSscript.js5.6How long the loading lasts, in screens of scroll.
BUMPscript.js1How high the top bag jumps at a tap or a click.

Try them in the Playground

When to use it

For the header of a hotel, a bed and breakfast or a holiday home, a luggage storage or delivery service, a luggage shop, a travel agency or a packing app; and for any page where scrolling fills something up, a cart, an order, a list.

On phones

The same animation with the sizes adapted: the cart and the bags are measured on the smaller side of the box, so the whole pile fits on the phone too; the lift and the palm stay at the sides, partly off the edge. A tap bumps the top bag and finger scrolling stays free.

With reduced motion

No falls, no bounces and no smooth scroll: the pile is computed silently on load and stays still, and as you scroll each bag fades in at its place; the bell and the needle stay put.

Pitfalls

  • Scroll must not move the bodies: it should only say how many there should be. If the bag’s position were tied to scrub, physics would have nothing to do and scrolling back would blow the pile apart.
  • The engine steps at a fixed rate (1/60 s accumulated), not at the frame delta: with a variable step on a 120 Hz screen the bags behave differently and the pile collapses.
  • A bag that leaves must be made static before moving it, or it pushes the others away as it rises.
  • For the reduced version the pile is prepared by running the engine silently for each bag, then the bodies become static: that way it matches the real one.

Libraries

GSAP 3.15.0, Lenis 1.3.26, Matter.js 0.20.0, Fraunces (font variabile) 5.3.0, Mona Sans (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 13 KB; the libraries: GSAP + ScrollTrigger 45 KB, Lenis 5.3 KB, Matter.js 25 KB.

Testing

Tested on 23 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 animations