Skip to content
animorio
IT
All components

The film lab pricing

Pricing, SVG, Hover, PhysicsGSAPAdvancedCompressed weight: Demo 13 KBGSAP 28 KBFreeDownload the zip

The pricing table of a film developing lab, with three plans: each one is a camera on a wooden shelf in front of a coloured backdrop, with its rolls of film beside it. Picking a plan fires the camera, with the flash and the shutter button going down, and a print drops onto the line and swings on its peg; switching to yearly billing a cardboard box slides in and the rolls hop into it one by one, while every price counts digit by digit to the year’s total.

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 film lab pricing</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/instrument-sans@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the plans of a film developing lab, each one a camera on a wooden shelf with its rolls of film beside it,
         drawn by the script. Picking a plan fires the camera's flash and a print slides up; paying yearly a
         cardboard year pack slides in and the rolls hop into it while each price counts to the year's total -->
    <section class="lab" aria-labelledby="lab-title" data-lab data-autoclic>
      <header class="lab__head">
        <p class="lab__brand">Silverbath Lab</p>
        <h2 class="lab__title" id="lab-title">Every roll, <em>developed.</em></h2>
        <div class="bill" role="radiogroup" aria-label="Billing" data-bill>
          <span class="bill__thumb" aria-hidden="true" data-thumb></span>
          <button class="bill__opt" type="button" role="radio" aria-checked="true" data-period="month">Monthly</button>
          <button class="bill__opt" type="button" role="radio" aria-checked="false" tabindex="-1" data-period="year">Yearly <span class="bill__free" data-free>2 months free</span></button>
        </div>
      </header>
      <div class="tabs" role="tablist" aria-label="Plans" data-tabs></div>
      <div class="plans" data-plans></div>
      <p class="sr" 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.

56 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 film lab pricing” from Animorio and fit it into my site

What it does

On sand-coloured paper, Silverbath Lab, the heading Every roll, developed. and a Monthly / Yearly switch (with the chip 2 months free); under it, three plans. Each has a little window with a coloured backdrop, a wooden shelf with its grain, its camera and its rolls: Roll (€6 a month) a yellow and green cardboard disposable camera with a ridged flash and one roll, Darkroom (€14, Most loved) a leather and chrome rangefinder with a flash on the hot shoe and three rolls, Studio (€29) a tan leather twin lens reflex with a bulb flash dish and six rolls of 120 stacked in a pyramid. Coming in, the cameras drop in with a bounce, the rolls fall into place and the prices count up from zero. The plan chosen has a red edge and the Your plan button with a tick; picking it fires the camera: the shutter button goes down, the flash bursts into rays, the lens blinks, the camera squashes on its feet and springs back, the disposable’s wheel winds on, and a print drops onto the line from above and swings on its wooden peg. Switching to Yearly a cardboard Year pack slides into every plan from the right and the rolls hop into it one by one with a somersault, while the label counts the rolls of the year (12, 36, 72) and every price counts to the year’s total (€60, €140, €290), the line under it becoming €5 a month · save €12. Back to Monthly the rolls hop out and bounce into place, and the box slides away. The pointer tilts the camera towards it and moves the light on its lens.

How it works

One scene per plan, in SVG

Each plan is an SVG of 260 by 160 units with preserveAspectRatio meet: the backdrop, the shelf and the floor are rectangles far wider than the box, so no window ever shows an empty edge. On top, in layers: the print on the line, the shadow, the shelf, the camera, the box and the rolls, the burst and the white veil. The cameras are hand drawn with chrome gradients, a dotted texture for the leather and stitching, and every moving part has its own attribute (data-shutter, data-glass, data-flashwin, data-wheel).

The rolls hopping into the box

The box has three layers: the back of the opening, an empty group for what goes inside and the front with its label. The rolls outside sit in a group above the front; each rises with power2.out to above the rim, and halfway through the hop a call() moves it into the inside group with appendChild, so the fall with power2.in passes behind the cardboard. Coming back it is the same in reverse, and the landing is bounce.out.

The flash

A short timeline: the shutter button goes down 1.6 units and comes back with back.out(3), the camera squashes to 0.955 and springs back with elastic.out(1, 0.35) around the point it stands on (svgOrigin), the lens drops to 0.25 opacity for a moment, the burst (a circle with a radial gradient and twelve rays) grows from 0.3 and fades, and a paper-coloured veil covers the window and fades in 0.45 seconds. FLASH sets how strong the burst and the veil are.

Prices that count

Each price is a row of slots, one per digit, with overflow hidden. A tween takes the value to the new price with power3.out and each step rewrites only the digit that changes, coming in from above when the number goes up and from below when it goes down. The three plans start eight hundredths apart.

The phone

Below 640 pixels the three plans share the same grid cell and only one shows, chosen with the tabs at the top: the new one slides in from the side with back.out and the old one slides out the other way. The lists of what is included hide so it fits the box.

Values to change

NameFileDefaultWhat it changes
PLANSscript.js[['Roll', '1 roll a month, developed', 6, […]], …]The plans: name, what it covers, price a month in euros, what comes with it.
FREE_MONTHSscript.js2The months a year given away to those paying yearly.
PICKscript.js1The plan chosen when the page opens (0 is the first).
LOVEDscript.js1The plan with the Most loved label (-1 for none).
COUNTscript.js1.3The seconds a price takes to count to the new one.
HOPscript.js0.42The seconds of each roll’s hop into the box or out of it.
FLASHscript.js1How bright the flash is: 0 none, 1.5 blinding.
TILTscript.js6How many degrees the camera leans towards the pointer.

Try them in the Playground

When to use it

For the pricing page of a photo lab, a print service, a studio or a photography app; and anywhere plans can be told with an object that grows from one plan to the next and yearly billing with a box that fills up.

On phones

One plan shows at a time, chosen with the Roll, Darkroom, Studio tabs at the top: the new plan slides in from the side with a bounce. The same cameras, the same hopping rolls and the same flash; the lists of what is included hide.

With reduced motion

No hops or bounces: the rolls move into the box and out of it at once, the prices change at once, the print fades in and the flash is only a pale glow that fades.

Pitfalls

  • A roll hopping into the box must pass in front of the rim going up and behind it coming down: with a single layer it is either always over the cardboard or never seen. It moves to the other group halfway through the hop with appendChild, at the top, where nobody notices.
  • GSAP reads the transform attribute of SVG groups and turns it into x and y: the rolls are born with translate(x y) and are then animated with x and y without losing their place.
  • Rebuilding every digit of the price at each step of the tween makes the number flicker: only the digit that changes is rewritten.
  • A backdrop drawn as big as the viewBox leaves empty bands at the sides when the window is wider or taller: it is drawn far bigger and left for the box to crop.

Libraries

GSAP 3.15.0, Fraunces (font variabile) 5.3.0, Instrument Sans (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 13 KB; the libraries: GSAP 28 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 components