Skip to content
animorio
IT
Back to the library

The fruit stall

Physics, HoverCanvasAdvancedCompressed weight: Demo 14 KBMatter.js 25 KBFreeDownload the zip

The header of a made-up greengrocer, in the rich style: under a red and cream striped awning the fruit falls into the wooden crates with real physics, apples, pears, oranges, lemons, cherry pairs and a watermelon, rolls and piles up; with the pointer or a finger you push it, grab it, throw it and carry it onto the brass scale, whose needle swings and settles on the 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 fruit stall</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/caveat@5.3.0/index.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 class="stall" data-stall>
    <!-- the header of a made-up greengrocer, Pippin Market: a striped awning with a scalloped fringe, wooden crates on
         a counter, chalk prices on little slates and a brass grocer's scale. The fruit falls from under the awning into
         the crates with real physics; push it with the pointer, grab it and throw it, or carry it onto the scale -->
    <canvas class="stall__canvas" data-canvas aria-label="A fruit stall: apples, oranges, lemons, pears, cherries and a watermelon in wooden crates, with a brass scale. Drag the fruit to move it, put it on the scale to weigh it." role="img"></canvas>

    <header class="stall__bar">
      <span class="stall__brand">Pippin Market</span>
      <span class="stall__hours">Open daily, seven to two</span>
    </header>

    <section class="stall__copy">
      <h1 class="stall__title">Picked this <br><em>morning.</em></h1>
      <p class="stall__lede">Boxes of seasonal fruit from the stall, at your door by noon.</p>
      <a class="stall__button" href="#order"><span>Order a box</span></a>
    </section>

    <p class="stall__hint" data-hint>Drag the fruit onto the scale</p>
    <p class="sr-only" aria-live="polite" data-weight-text></p>

    <ul class="sr-only">
      <li>Apples, 2.40 a kilo</li>
      <li>Oranges, 1.90 a kilo</li>
      <li>Pears and lemons, 3 for 1</li>
    </ul>
  </main>

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

31 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 fruit stall” from Animorio and fit it into my site

What it does

Under Pippin Market’s red and cream striped awning, with the scalloped fringe barely swaying, three wooden crates wait on the counter, each with its little slate hung on string and the price written in chalk; to one side, a brass scale with a dial. From above, from under the awning, the fruit falls into the crates one piece at a time: red and green apples with their stalk and leaf, long-necked pears, oranges with pitted peel and their calyx, pointed lemons, cherries in pairs on the same stalk, and last a striped watermelon that settles on top of the pile. They roll, bounce and pile up; the boards across the front of the crates hide part of them, and through the gap in the middle you see the fruit inside. Moving the pointer through the fruit pushes it; pressing and holding grabs it, you carry it around and throw it, and it lands wherever it lands. Put it on the scale pan and the red needle swings and settles on the weight. Left alone, the stall plays by itself: now and then a fruit jumps up or goes onto the scale, and when the pan is full it goes back to its crate.

How it works

One canvas, two layers

The wall, the poles, the counter, the inside of the crates and the scale column only change when the size changes: they are drawn once on a hidden canvas and copied with drawImage every frame. On top, in order: the back of the pan, the fruit, the boards across the crates with their slates, the pan rim with the dial and last the awning, so falling fruit appears from under the fringe.

The physics: circles and one constraint

Each fruit is a Matter.js circle; the cherries are two circles tied by a soft constraint, so they hang and turn like a real pair. Each crate is three static walls (floor and two sides), the scale pan a static shelf with two rims. The physics runs at a fixed step, a sixtieth of a second, with an accumulator: with variable steps Matter becomes unstable.

The drawing turns with the body, the light does not

Each fruit is drawn in its body’s frame (position and angle), so stalk, leaf, calyx, stripes and pores turn with it and stay attached. The gradient that gives the volume is computed in screen space instead, with the light always top left: a rolling fruit changes face but not light. For the cherries, the point where the stalks meet is computed every frame above the middle of the pair, and the leaf grows from there.

Grab, push, throw

Pressing on a fruit creates an elastic constraint between the grabbed point and the pointer: the fruit follows the finger a little late and, released, keeps its speed, so it gets thrown. Moving through the fruit without pressing gives every nearby fruit a small force in the pointer’s direction, stronger the closer it is.

The scale

Every frame the grams of the fruit resting on the pan are added up (an apple 180, an orange 200, a lemon 110, a cherry 8). The needle is a spring: its speed takes a share of the distance to the weight and loses some every step, so it swings and settles. The pan sinks a few pixels with the weight.

The autopilot

If nobody touches it for six seconds, every PACE seconds the stall makes a move: a fruit jumps up, or the top fruit is carried onto the scale along a curve, pulled by a moving constraint; with three fruits on the pan one goes back to its crate. The randomness has a fixed seed, so every round is the same.

Values to change

NameFileDefaultWhat it changes
GRAVITYscript.js1.15Gravity: higher, the fruit falls and settles faster.
BOUNCEscript.js0.32How much the fruit bounces.
FRUITscript.js36How many fruits on a computer; fewer than half on a phone.
DROPscript.js70Milliseconds between one fruit and the next as they fall.
PUSHscript.js1How hard the pointer pushes the fruit when passing through it (0 none).
PACEscript.js3.2Seconds between the autopilot’s moves.
SCALE_MAXscript.js2The kilos at the end of the scale’s dial.
--awning, --wood, --brass…style.cssrosso, legno, ottoneThe colours in :root: the canvas reads them again at every resize.

Try them in the Playground

When to use it

For the header of a greengrocer, a farmers’ market, an online fruit and veg shop or a box delivery service. It is a toy: the real prices also need to be in plain text.

On phones

Same physics and same drawing, adapted sizes: the words sit on top across the width, the crates and the scale below, and there is less than half the fruit. A finger grabs and throws; the canvas has touch-action none.

With reduced motion

No falling or bouncing: the fruit is already in the crates, still (the physics runs once before the first frame), with two cherries on the pan and the needle already on the weight.

Pitfalls

  • A fruit’s drawing must turn with its body, the light gradient must not: rotate everything and the light turns with the fruit, which looks like a rolling ball.
  • The cherries are two bodies: the point where the stalks meet must be recomputed every frame from both positions, or the stalks come apart.
  • Fixed-step physics: Engine.update with the real frame time makes the piles tremble.
  • On resize the fruit must be scaled (Body.scale) and put back in proportion above the counter, or it ends up outside the new crates.
  • The background is redrawn only on resize: redrawing wood and bricks every frame is expensive on a phone.

Libraries

Matter.js 0.20.0, Fraunces (font variabile) 5.3.0, Caveat (font variabile) 5.3.0, Mona Sans (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 14 KB; the libraries: Matter.js 25 KB.

Testing

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