Skip to content
animorio
IT
All components

The lenticular button

Buttons, Hover, 3DCanvas, GSAPAdvancedCompressed weight: Demo 10.0 KBGSAP 28 KBFreeDownload the zip

The Pre-order button of a shop printed like a lenticular postcard: its face is a row of thin lenses, three-sided prisms with one picture on each face. From the left it says Pre-order, from the right Ships Nov 14, and as the pointer goes by the lenses turn over in a wave, with the stripy flicker and the light running along the crests; pressed, every lens turns on to Reserved, with a tick that draws itself.

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 lenticular button</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bricolage-grotesque@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the lenticular button: the Pre-order button of a shop, printed like a lenticular postcard. Its face is a row of
         thin ribbed lenses, each a little prism with three pictures on it, one per face: seen from the left the card
         says Pre-order, from the right Ships Nov 14, and a press turns every lens over to Reserved. The lenses and
         their light are drawn by the script on the canvas -->
    <div class="lenti" data-lenti>
      <button class="lenti__card" type="button" data-card data-autoclic>
        <canvas class="lenti__lenses" aria-hidden="true" data-lenses></canvas>
        <span class="lenti__sheet" aria-hidden="true"></span>
        <span class="sr" data-name></span>
      </button>
      <p class="sr" aria-live="polite" data-live></p>
    </div>
  </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.

20 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 lenticular button” from Animorio and fit it into my site

What it does

On a black background a card of finely ribbed plastic, with rounded corners and a thread of light along its top edge, like a lenticular postcard: ultramarine, with a shopping bag and Pre-order in cream, in Bricolage Grotesque. A band of fine glints, one per lens, rests on the ribs. With the mouse over it, the band of light follows the pointer running along the crests and the lenses lean a little, so the ribs show more. Past the middle, the lenses turn over in a wave that starts from the pointer, each on its own spring and a hair late or early on its neighbours: for a moment the two pictures alternate in stripes, blue and orange, as on a real postcard, and the card turns orange with a parcel and Ships Nov 14. Going back to the left, or leaving, the wave brings it back to Pre-order. Pressed, the card squashes a little and every lens turns forward, from the point pressed towards the edges, past the orange to the third picture, card white, with Reserved in midnight blue: a blue tick draws itself as the last lenses land and a band of light runs once over all the crests. After a moment the lenses finish their turn, from one end to the other, and go back to the picture the pointer asks for. When the page opens the card arrives turned towards Ships Nov 14 and the lenses settle on Pre-order from the left.

How it works

Three pictures cut into strips

Each picture (background, icon, words) is painted once on a canvas of its own, at the screen's density, and cut into strips as wide as a lens (LENS pixels, adjusted so a whole number fits). Each lens is a three-sided prism with an upright axis: face k shows its strip of picture k, with drawImage from the right piece. With all the lenses turned to the eye the picture is whole again; halfway round each lens shows two, squeezed, and the pictures alternate in stripes.

A prism seen from the front

For a lens turned by φ degrees, face k has its normal at θ = φ − 120·k (brought between −180 and 180) and shows if cos θ is positive. Its middle moves by a·sin θ (a is the triangle's apothem, width / 2√3) and its width becomes width · cos θ: the strip is drawn on those measures. Between one lens and the next, while they turn, the dark back of the card shows.

The light worked out for each face

Each face bulges a little: its normal goes from θ − 14° to θ + 14° from one edge to the other. At three points of the face the soft light of the sky, coming from the left, gives ambient + diffuse · cos(θ − sky), divided by that of the face turned straight: below 1 it is a black veil, above 1 a white one, in a gradient along the face. So at rest the ribs show, and a lens that turns darkens as it turns away.

The glint on the crests

For each lens the vector halfway between the direction of the lamp and that of the eye, which sits where the pointer is, is worked out (Blinn's half vector). A face glints where its bulge turns the normal along that vector: if the two angles are within 9°, a thin white line falls at the right point of the face, strongest in the middle, with a halo and a soft veil round it. As the angle changes slowly along the card, the lines make a band that follows the pointer and jumps from lens to lens as they turn.

The waves and the springs

Each lens has its spring (stiffness SPRING, damping from BOUNCE, two half steps a frame, never longer than a thirtieth of a second) and a move waiting with its time on GSAP's clock: a lens starts when its time comes, later the farther it is from the pointer or the point pressed, plus a delay of its own up to 0.07 seconds, so the front of the wave is ragged like a real print's. The press takes every lens to 240 degrees by way of 120, and the way back takes them on to 360 (or back to 120, if the pointer is on the right): at the end 360 degrees are taken off all of them, which is the same angle.

The pointer, the finger, the keys

The pointer leans the lenses by TILT degrees at most and moves the eye; the picture changes only 6 per cent past the middle, so a hand resting in the middle does not make it flicker. A finger dragged across does the same (touch-action: pan-y), and the click that follows a drag is not a press. The focus from the Tab key shows the second picture; Enter and Space press the button, and squash it by themselves because there is nothing to let go of.

Values to change

NameFileDefaultWhat it changes
FIRSTscript.jsPre-orderThe first picture's words: at rest, and seen from the left.
SECONDscript.jsShips Nov 14The second picture's words: seen from the right, or reached with the Tab key.
THIRDscript.jsReservedThe third picture's words, after a press, with the tick.
LENSscript.js5How wide a lens is, in pixels: the pictures are cut into strips this wide.
TILTscript.js16How far the lenses lean following the pointer, in degrees.
WAVEscript.js0.5The seconds a press takes to turn the lenses from one end of the card to the other.
SPRINGscript.js240How stiff each lens's spring is: higher, snappier turns.
BOUNCEscript.js0.42How far a lens swings past its picture before it settles: 0 not at all.
GLINTscript.js1The light on the lens crests: 0 none, 2 twice as bright.
HOLDscript.js1.8The seconds Reserved stays before the card turns back.

Try them in the Playground

When to use it

For the button of a pre-order or a release on a set date: a record, a book, a print, a pair of shoes, a limited run. It belongs where printing, souvenirs and collectables belong, and where two short pieces of information (the action and the date) are better in the same place than one under the other. Short words, up to about fifteen letters: the type shrinks by itself to fit. It is a button to show off, one to a page.

On phones

The card is 60 pixels tall like the other buttons and 300 wide, or the screen minus 48; the lenses are counted again for the real size. With no mouse a finger dragged across leans the card and turns it like the pointer (touch-action: pan-y, so up and down the page still scrolls), and a moment after it lifts the card goes back to Pre-order; a short tap is the press, and the wave starts from where the finger touched.

With reduced motion

The lenses do not turn or lean, and the band of light stays still. The pictures go from one to the other in a fade (a third of a second), with the pointer on the right or the focus as with the press; Reserved arrives with the tick already drawn and when the page opens Pre-order is already there. Pressed, the card darkens a little instead of squashing.

Pitfalls

  • Leaning towards the pointer and moving the eye shifted the band of glints in opposite directions, and the band stayed almost still: the lenses lean the other way, and now the light follows the hand.
  • With the glints taken from a faraway lamp and a wide bulge (24 degrees) every lens glinted, and the card looked striped rather than glossy: with the lamp and the eye close and a bulge of 9 degrees the angle changes enough along the card to leave a single band. The diffuse light comes from a fixed direction instead, because the nearby lamp darkened one end of the card more than the other.
  • After a whole turn a lens's angle is 360 degrees and its glint disappeared, because its difference from Blinn's vector was no longer between −180 and 180: each face's angle is always brought back into that range.
  • A clean wave front looked like a blind, not a lenticular print: a different delay for each lens (up to 0.07 seconds) makes it ragged, and the two pictures alternate in stripes like on a real postcard.
  • The white glint on the third picture, card white, did not show: in the band of light of the press each crest throws a hair of shade beside its glint, and the band reads on the pale picture too.
  • The tick drawn in the same box as the other two icons looked like a pencil mark: with no box round it, it has to be drawn larger and thicker (22 pixels instead of 17).

Libraries

GSAP 3.15.0, Bricolage Grotesque (font variabile) 5.3.0.

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

Testing

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