Skip to content
animorio
IT
Back to the library

The layer cake that builds as you scroll

Scroll, SVGGSAPAdvancedCompressed weight: Demo 19 KBGSAP + ScrollTrigger 45 KBLenis 5.3 KBFreeDownload the zip

Scroll inside the preview

A cake shop’s page: as you scroll, a cake is built on a slowly turning stand. The first sponge drops and bounces, the spatula spreads the cream, berries rain down, the second sponge lands, chocolate glaze runs down the sides in fat drips, the piping bag adds rosettes and the candles go in, lit. At the end two cuts, and a slice slides out and turns to show its layers. Scroll back up and the cake comes apart.

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="it">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>La torta a strati</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/standard.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <!-- la testata della pasticceria inventata -->
  <header class="testata">
    <p class="marchio">Bramble <span>&amp; Butter</span></p>
    <nav class="menu" aria-label="Main">
      <a href="#torta">Cakes</a>
      <a href="#fine">Order</a>
      <a href="#fine">Visit</a>
    </nav>
  </header>

  <main>
    <!-- l'apertura -->
    <section class="apertura">
      <p class="apertura__etichetta" data-entra>A small cake shop on Mill Lane</p>
      <h1 class="apertura__titolo">
        <span class="riga"><span data-titolo>Built</span></span>
        <span class="riga"><span data-titolo><em>layer by layer.</em></span></span>
      </h1>
      <p class="apertura__testo" data-entra>Our Sunday sponge, from the first bounce to the last candle. Scroll down and watch it rise on the stand.</p>
      <p class="apertura__cenno" data-entra>
        Scroll to build
        <svg viewBox="0 0 24 32" aria-hidden="true"><path d="M12 3V27M4 19L12 28L20 19" /></svg>
      </p>
      <div class="ciliegie" aria-hidden="true">
        <svg class="ciliegia" viewBox="0 0 60 90" data-ciliegia><path d="M31 46C30 30 34 16 46 6" class="ciliegia__gambo" /><path d="M46 6C52 8 56 13 55 18C49 18 46 13 46 6Z" class="ciliegia__foglia" /><circle cx="30" cy="64" r="22" class="ciliegia__corpo" /><path d="M17 58C18 52 22 48 27 47" class="ciliegia__luce" /></svg>
        <svg class="ciliegia" viewBox="0 0 60 90" data-ciliegia><path d="M31 46C30 30 34 16 46 6" class="ciliegia__gambo" /><path d="M46 6C52 8 56 13 55 18C49 18 46 13 46 6Z" class="ciliegia__foglia" /><circle cx="30" cy="64" r="22" class="ciliegia__corpo" /><path d="M17 58C18 52 22 48 27 47" class="ciliegia__luce" /></svg>
        <svg class="ciliegia" viewBox="0 0 60 90" data-ciliegia><path d="M31 46C30 30 34 16 46 6" class="ciliegia__gambo" /><path d="M46 6C52 8 56 13 55 18C49 18 46 13 46 6Z" class="ciliegia__foglia" /><circle cx="30" cy="64" r="22" class="ciliegia__corpo" /><path d="M17 58C18 52 22 48 27 47" class="ciliegia__luce" /></svg>
      </div>
    </section>

    <!-- la torta: il palco resta fermo e, scorrendo, si monta strato dopo strato -->
    <section class="storia" id="torta" data-storia>
      <div class="palco">
        <div class="racconto">
          <nav class="ricetta" aria-label="Steps">
            <button type="button" data-voce>Sponge</button>
            <button type="button" data-voce>Cream</button>
            <button type="button" data-voce>Berries</button>
            <button type="button" data-voce>Stack</button>
            <button type="button" data-voce>Glaze</button>
            <button type="button" data-voce>Pipe</button>
            <button type="button" data-voce>Candles</button>
            <button type="button" data-voce>Slice</button>
          </nav>

          <div class="passi">
            <article class="passo" data-passo>
              <p class="passo__etichetta" data-linea>The sponge</p>
              <h2 class="passo__titolo">
                <span class="riga"><span data-riga>Drop the</span></span>
                <span class="riga"><span data-riga>first <em>layer.</em></span></span>
              </h2>
              <p class="passo__testo" data-linea>Vanilla sponge, baked the night before so it holds its shape. It lands, it bounces, it settles.</p>
            </article>
            <article class="passo" data-passo>
              <p class="passo__etichetta" data-linea>The cream</p>
              <h2 class="passo__titolo">
                <span class="riga"><span data-riga>Spread it thin,</span></span>
                <span class="riga"><span data-riga>turn the <em>stand.</em></span></span>
              </h2>
              <p class="passo__testo" data-linea>Mascarpone from the middle out. The stand does the turning, the spatula stays still.</p>
            </article>
            <article class="passo" data-passo>
              <p class="passo__etichetta" data-linea>The berries</p>
              <h2 class="passo__titolo">
                <span class="riga"><span data-riga>Let it rain</span></span>
                <span class="riga"><span data-riga><em>berries.</em></span></span>
              </h2>
              <p class="passo__testo" data-linea>Raspberries, blueberries and strawberries, wherever they land. No two cakes look the same inside.</p>
            </article>
            <article class="passo" data-passo>
              <p class="passo__etichetta" data-linea>The stack</p>
              <h2 class="passo__titolo">
                <span class="riga"><span data-riga>Second layer,</span></span>
                <span class="riga"><span data-riga><em>gently.</em></span></span>
              </h2>
              <p class="passo__testo" data-linea>Just enough weight for the cream to peek out at the edges, and no more.</p>
            </article>
            <article class="passo" data-passo>
              <p class="passo__etichetta" data-linea>The glaze</p>
              <h2 class="passo__titolo">
                <span class="riga"><span data-riga>Pour it warm,</span></span>
                <span class="riga"><span data-riga>let it <em>drip.</em></span></span>
              </h2>
              <p class="passo__testo" data-linea>Dark chocolate, warm but never hot, so it rolls over the edge in slow, fat drips.</p>
            </article>
            <article class="passo" data-passo>
              <p class="passo__etichetta" data-linea>The piping</p>
              <h2 class="passo__titolo">
                <span class="riga"><span data-riga>One swirl</span></span>
                <span class="riga"><span data-riga>per <em>turn.</em></span></span>
              </h2>
              <p class="passo__testo" data-linea>Raspberry buttercream round the rim. The bag stays put and the stand brings every spot to it.</p>
            </article>
            <article class="passo" data-passo>
              <p class="passo__etichetta" data-linea>The candles</p>
              <h2 class="passo__titolo">
                <span class="riga"><span data-riga>Light them</span></span>
                <span class="riga"><span data-riga><em>all.</em></span></span>
              </h2>
              <p class="passo__testo" data-linea>Striped candles in the middle. Make a wish before the knife comes out.</p>
            </article>
            <article class="passo" data-passo>
              <p class="passo__etichetta" data-linea>The slice</p>
              <h2 class="passo__titolo">
                <span class="riga"><span data-riga>Two cuts,</span></span>
                <span class="riga"><span data-riga>lift it <em>out.</em></span></span>
              </h2>
              <p class="passo__testo" data-linea>Sponge, cream, berries, sponge, glaze. Scroll back up and the cake comes apart again.</p>
            </article>
          </div>

          <p class="misura">On the stand <strong><span data-peso>0</span> g</strong></p>
        </div>

        <svg class="scena" data-scena viewBox="0 130 600 630" preserveAspectRatio="xMidYMax meet" aria-hidden="true">
          <defs>
            <linearGradient id="grad-alzata" x1="0" y1="0" x2="1" y2="0">
              <stop offset="0" class="stop-alzata-scura" />
              <stop offset="0.4" class="stop-alzata" />
              <stop offset="0.6" class="stop-alzata-chiara" />
              <stop offset="1" class="stop-alzata-scura" />
            </linearGradient>
            <linearGradient id="grad-acciaio" x1="0" y1="0" x2="1" y2="1">
              <stop offset="0" class="stop-acciaio-chiaro" />
              <stop offset="0.5" class="stop-acciaio" />
              <stop offset="1" class="stop-acciaio-scuro" />
            </linearGradient>
            <linearGradient id="grad-rame" x1="0" y1="0" x2="1" y2="0">
              <stop offset="0" class="stop-rame-scuro" />
              <stop offset="0.45" class="stop-rame" />
              <stop offset="1" class="stop-rame-scuro" />
            </linearGradient>
          </defs>

          <!-- l'alzata: piede, gambo e piatto; le scanalature del bordo girano -->
          <g data-alzata>
            <ellipse cx="300" cy="744" rx="150" ry="16" class="ombra-alzata" />
            <path d="M195 718V730A105 22 0 0 0 405 730V718Z" fill="url(#grad-alzata)" />
            <ellipse cx="300" cy="718" rx="105" ry="22" class="alzata-sopra" />
            <path d="M276 600C276 640 286 660 286 680C286 700 240 706 222 716C250 726 350 726 378 716C360 706 314 700 314 680C314 660 324 640 324 600Z" fill="url(#grad-alzata)" />
            <path d="M100 590V604A200 60 0 0 0 500 604V590Z" fill="url(#grad-alzata)" />
            <path class="scanalature" data-scanalature />
            <ellipse cx="300" cy="590" rx="200" ry="60" class="alzata-sopra" />
            <ellipse cx="300" cy="590" rx="186" ry="54" class="alzata-incavo" />
          </g>

          <!-- la torta (i pezzi li disegna lo script) -->
          <ellipse class="ombra-torta" data-ombra />
          <g data-torta></g>
          <g data-fetta></g>

          <!-- gli attrezzi -->
          <g data-spatola>
            <path d="M150 0C186 -4 196 -30 214 -54" class="spatola-collo" />
            <path d="M0 -5C-7 -5 -9 5 0 6L156 7C162 7 164 -6 156 -6Z" fill="url(#grad-acciaio)" class="spatola-lama" />
            <path d="M206 -46L282 -100" class="spatola-manico" />
          </g>
          <g data-pentola>
            <g data-pentola-corpo>
              <path d="M-96 18L-196 4" class="pentola-manico" />
              <path d="M2 -3C-4 0 -8 3 -10 8L-12 52C-12 62 -20 68 -30 68H-86C-96 68 -104 62 -104 52L-104 6C-104 2 -100 0 -96 0H-14C-8 -2 -3 -4 2 -3Z" fill="url(#grad-rame)" class="pentola" />
              <path d="M-100 4H-12" class="pentola-bordo" />
            </g>
          </g>
          <path class="filo-glassa" data-filo />
          <g data-sac>
            <path d="M-8 -16C-30 -56 -44 -104 -26 -142L44 -152C58 -112 40 -56 8 -16Z" class="sac" />
            <path d="M-26 -142C-14 -150 30 -158 44 -152L48 -166C30 -172 -12 -166 -28 -156Z" class="sac-nodo" />
            <path d="M-14 -40C-2 -38 8 -40 18 -46" class="sac-piega" />
            <path d="M0 2L-9 -18H9Z" fill="url(#grad-acciaio)" />
          </g>
          <g data-coltello-clip-g>
            <clipPath id="taglio"><path data-coltello-clip /></clipPath>
            <g clip-path="url(#taglio)">
              <path class="coltello-lama" fill="url(#grad-acciaio)" data-lama />
              <path class="coltello-manico" data-manico />
            </g>
          </g>

          <!-- dove il puntatore fa tremare la torta -->
          <ellipse cx="300" cy="540" rx="210" ry="150" class="presa" data-presa />
        </svg>
      </div>
    </section>

    <footer class="fine" id="fine">
      <p class="fine__frase">Your slice is waiting. <em>Come by on Sunday.</em></p>
      <a class="fine__bottone" href="#torta"><span>Order a cake</span></a>
    </footer>
  </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="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.

98 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 layer cake that builds as you scroll” from Animorio and fit it into my site

What it does

The page of a made-up cake shop, Bramble & Butter, on a cream background. At the top a big title, «Built layer by layer.», and three cherries that drop in with a bounce and then sway on their stems; the pointer makes them hop. Below, the stage stops: on the right a mint-green cake stand seen slightly from above, with the fluting on its rim turning, on the left the story in eight steps, with their names at the top (Sponge, Cream, Berries, Stack, Glaze, Pipe, Candles, Slice) and the grams on the stand counting up. As you scroll, the first sponge drops from above, bounces and squashes with a wobble, while its shadow tightens on the plate. An offset spatula comes in and lies on the sponge from the middle to the edge: the stand turns underneath it, and the cream spreads in circles until it peeks over the edge, leaving the spatula’s marks. Raspberries, blueberries and strawberry halves rain down, each with its own bounce and spin. The second sponge drops, and when it squashes, the cream underneath bulges out. A small copper pan comes in, tips and pours a thread of chocolate glaze into the middle: the glaze spreads over the top and then rolls down the sides in fat drips, each one its own length, with a drop at the end. The piping bag stops over one spot of the rim and squeezes for every rosette, while the stand goes round once and brings every spot under the nozzle. The striped candles drop into the middle with a small bounce and light up one after the other, their flames trembling. At the end a knife comes down twice, leaving a mark where it cuts; then the slice is pulled forward on a cake server, the stand moves to the left and the slice turns until its cut faces us: sponge, cream with berries, sponge, glaze. The cake keeps the gap, with the layers showing there too, and the grams go down. Scroll back and everything runs in reverse. The pointer on the cake makes it wobble like jelly; the step names jump to their part.

How it works

The still stage

The section is SCROLL_LENGTH screens tall and holds a sticky stage 100lvh tall: the story on the left, on the right an SVG with a fixed viewBox and preserveAspectRatio xMidYMax meet, so the stand stays sitting at the bottom at any size. The stand is drawn once; the cake, the berries, the rosettes and the candles are made by the script, and the tools (spatula, pan, piping bag, knife) sit on top of everything.

A timeline of numbers

Everything that moves is a number in one object: how far each sponge has fallen, how much it wobbles, how far the cream has spread, the glaze, the drips, the piping bag, the turn of the stand, the knife, the slice. A single paused timeline moves them, each with its own curve (bounce.out for the sponges and the berries, back.out for the tools coming in, elastic.out for the flames). A tween takes its time from 0 to the end with a scrubbed ScrollTrigger: scroll up and the timeline goes back and the cake comes apart.

A cylinder seen from above

Every layer is a squashed cylinder: an ellipse on top (the radius times 0.3 in height) and the band in front, the angles from 0 to 180 degrees. A GSAP ticker rebuilds the paths every frame from the bottom, height and radius of each layer: a sponge landing drops and widens on a damped sine. Everything on the cake (crumbs, berries, drips, rosettes, candles) has its own angle: the stand’s turn is added, and what goes round the back disappears or is sorted behind the rest.

The piping bag stays put

The rosettes don’t chase the bag: the stand turns a full 360 degrees while the bag stays over one spot of the rim. Each rosette’s angle is worked out so that it passes under the nozzle exactly when its turn comes, and it grows there with elastic.out. The candles sit on a small circle, turned so that at the end none of them is where the knife will go.

The slice

After the two cuts the same function draws two pieces: the cake with the gap (the angles from 108 to 432 degrees) and the slice (from 72 to 108). Each piece has the faces of its cut, quadrilaterals from the centre to the edge, shown only when they face us, with the berries and the sponge’s holes inside. The slice moves and its angle drops by 108 degrees: at the end its cut faces us straight on. The knife is trimmed by a clipPath that hides the part already inside the cake.

Values to change

NameFileDefaultWhat it changes
SCROLL_LENGTHscript.js7How many screens of scrolling the whole cake takes: more is slower.
BERRIESscript.js18How many berries rain on the cream.
DRIPSscript.js14How many drips of glaze run down the sides.
DRIP_LENGTHscript.js1How far the drips run: 0.4 short, 1.6 almost to the plate.
ROSETTESscript.js12How many buttercream rosettes round the rim.
CANDLESscript.js5How many candles in the middle (7 at most, so the knife can pass between two).
SPINscript.js1How far the stand turns while you scroll: 0 keeps it still (it still goes round once for the rosettes).
SQUASHscript.js1How much the sponges squash and wobble when they land; next to it is CAKE_GRAMS for the counter.
--glassastyle.css#4a2216The colour of the glaze; next to it are those of the sponge, the cream, the berries, the rosettes, the candles and the stand.

Try them in the Playground

When to use it

For anyone who sells or tells about something built in layers: a cake shop, made-to-order cakes, a caterer, a cake decorating course, a recipe to follow step by step, but also a product made of stacked parts, like a sandwich, a mattress or a plan with its tiers.

On phones

The same animation with the sizes adapted: the story sits at the top, smaller, with the step names on two lines and the grams in small type below, and the stand in the room left, resting on the bottom of the screen. A tap on the cake makes it wobble, a tap on the cherries makes them hop.

With reduced motion

No falling, no dripping, no turning, no smooth scrolling: at every step the cake and the text fade out and come back with the step already done (the sponge in place, the cream spread, the berries, the glaze run down, the rosettes, the candles lit with still flames, the slice out). The opening title and cherries fade in too.

Pitfalls

  • A path shaped like the whole cake, with a piece «hidden» on top, can’t let a slice come out: the gap would stay full. The same function draws any sector of a cylinder (from one angle to another), with the faces of its cut shown only when they face us (the cosine of the angle says which way they point). Before the cut it draws the whole cake, after it two sectors that fit together.
  • The berries, rosettes and candles sit in a group per piece: after the cut, the ones that fall in the slice move into the slice’s group (appendChild), otherwise they would hang in the gap. Scroll back to before the cut and they go back into the cake.
  • With the rosettes and candles in a fixed order, the ones at the back covered the ones in front as the stand turned. Every frame they are sorted by depth (the sine of the angle), but put back into the DOM only when the order has changed.
  • With reduced motion, stopping the timeline at the end of a step left the text half gone (the lines leave half a second before the next step). The text of the step shown is set visible by hand, and the others hidden.
  • A candle could end up right where the knife goes and be cut in half. The candles are turned so that the middle of the slice falls between two of them, and there are seven at most, so the nearest one stays out of the cut.

Libraries

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

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

Testing

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