
The holographic card
Cards, 3D, Hover
Free
The card of a mountain loop whose map is made of paper sheets cut along the contour lines: as the pointer comes near, the sheets rise one after another on springs and the map becomes a relief model seen from where the pointer is, with cut edges and shadows. Preview route flies over the loop: the camera comes close and follows a marker that climbs the sheets step by step, while the distance and the ascent count up with it.
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 relief map card</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/geologica@5.3.0/full.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the card of a mountain loop. Its map is a stack of paper sheets cut along the contour lines: as the pointer
comes near, the sheets rise one after another into a relief seen from where the pointer is. Preview route
flies over the loop with a marker that climbs the sheets. The map is drawn by the script -->
<article class="trail" aria-labelledby="trail-title" data-trail>
<div class="trail__map" data-map-box>
<canvas class="trail__canvas" role="img" aria-label="Relief map of the loop: from Larch Lake up the ridge to Cairn Peak, down the east valley and back to the lake" data-map></canvas>
</div>
<div class="trail__body">
<h2 class="trail__title" id="trail-title" data-title>Cairn Ridge Loop</h2>
<p class="trail__where">A day loop from Larch Lake</p>
<dl class="trail__facts">
<div>
<dt>Distance</dt>
<dd><span data-count>11.4</span><span class="trail__unit"> km</span></dd>
</div>
<div>
<dt>Ascent</dt>
<dd><span data-count>720</span><span class="trail__unit"> m</span></dd>
</div>
</dl>
<button class="go" type="button" data-go>
<span class="go__fill" aria-hidden="true" data-fill></span>
<span class="go__icon" aria-hidden="true">
<span class="go__glyph go__glyph--play" data-icon="play"><svg viewBox="0 0 24 24"><path d="M8.5 5.8v12.4a.8.8 0 0 0 1.2.7l9.6-6.2a.8.8 0 0 0 0-1.4L9.7 5.1a.8.8 0 0 0-1.2.7z"/></svg></span>
<span class="go__glyph" data-icon="cancel"><svg viewBox="0 0 24 24"><path d="M7 7l10 10M17 7L7 17"/></svg></span>
<span class="go__glyph" data-icon="again"><svg viewBox="0 0 24 24"><path d="M18.6 13.2a6.8 6.8 0 1 1-1.9-5.9"/><path d="M17.6 3.8v4.1h-4.1"/></svg></span>
</span>
<span class="go__label" data-label>Preview route</span>
</button>
<p class="sr" aria-live="polite" data-live></p>
</div>
</article>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="script.js"></script>
</body>
</html>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
With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:
Take “The relief map card” from Animorio and fit it into my siteThe relief is a height function: a ridge (a Gaussian along a segment), the summit, a shoulder and two hollows for the lakes, all bent by an octave noise that shifts the coordinates and adds detail. The script works it out on a grid, a point every 1.5 pixels, together with the steepness; then every pixel of the sheets takes its height and steepness by interpolation, once.
For each sheet, (height − level) / steepness is the distance in pixels from the contour line: from it come the one-pixel soft edge, the brown line along the cut, two faint lines printed on the sheet and the paper grain. Every sheet becomes three images: the face, the edge (the same shape, darker) and the shadow (the shape blurred with shadowBlur, drawn off the canvas so only the shadow stays). The sheets are cut one per frame while they fall in at the start.
The eye stands EYE pixels above a point E of the map: a sheet z high is drawn grown by EYE / (EYE − z) round E, so the high sheets grow and slide away from the eye. The edge is the dark shape repeated at the heights between the sheet below and its own, just enough to leave no gaps; the shadow falls on the sheet below, moved away from the light. E follows the pointer, stretched and moved a little south, so with the pointer in the middle the relief is already seen at a slant.
The pointer’s distance from the map, over REACH pixels, says how far to raise it. Every sheet has its own spring with SPRING, worked out in steps of 4 milliseconds, and the higher the sheet the softer its spring (LAG): the stack opens from the bottom and the summit arrives last. A sheet stands RISE pixels over the one below for its lift; at the start it falls from a little higher as it appears.
The trail is a closed Catmull-Rom curve, measured a point per pixel; every point knows which sheet it lies on. The stretches are drawn right after their sheet, so the higher sheets cover them where they should, and one point more at each end draws the step. The white thread runs only along the sheet, the red also over the step. The dashes have a lineDashOffset equal to the length walked, so the pattern carries on from one sheet to the next; with the button pointed at, it slides forward.
A GSAP timeline takes the share of the loop from 0 to 1 in FLIGHT seconds. The camera comes close up to ZOOM and follows the arrow on springs, the eye stays behind it, the arrow hops onto each sheet on a spring of its own. Distance counts with the length walked, Ascent with the climb gathered along the height field, and the totals stay the words of the page. Stopped, the trail rubs itself out backwards and the facts go back to the totals.
| Name | File | Default | What it changes |
|---|---|---|---|
SHEETS | script.js | 12 | How many sheets of paper the relief is cut into. |
RISE | script.js | 3.8 | How high each sheet stands over the one under it, in pixels. |
SPRING | script.js | [150, 0.42] | The sheets’ spring: how stiff it is and how much it is held back (1 = no bounce). |
LAG | script.js | 0.55 | How much later the top sheet answers than the lowest (0 = all together). |
EYE | script.js | 460 | How high the eye is over the map, in pixels: lower gives a stronger relief. |
REACH | script.js | 460 | How far from the map, in pixels, the pointer still raises it. |
FLIGHT | script.js | 6.5 | How many seconds the flight over the route lasts. |
ZOOM | script.js | 1.4 | How close the camera comes during the flight. |
SUMMIT | script.js | ['Cairn Peak', '2,214 m'] | The summit’s name and height on the map. |
LAKE | script.js | 'Larch Lake' | The lake’s name on the map. |
WORDS | script.js | ['Stop preview', 'Preview again'] | The button’s words during the flight and after it. |
For a route card in a hiking, running or cycling app; for a mountain hut, a park, a trail race, an island to visit: wherever a place has a climb to tell.
The card is 340 pixels wide, or the screen minus 32, and the map is drawn again at the real size. A finger resting on the map raises it and looks at it from the finger’s side (the map has touch-action: none, so over it the finger does not scroll the page), moving it goes round the relief and, let go, the sheets settle. Preview route is tapped like any button and the flight is the same.
The relief stands already raised, seen from the south, and does not follow the pointer; no sheets falling in at the start and no walking dashes. Preview route does not fly: the trail turns solid red in a fade and the button moves to Preview again; the button’s words and icons change in a fade, without sliding.
GSAP 3.15.0, Geologica (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 15 KB; the libraries: GSAP 28 KB.
Tested on 6 October 2026
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.
With your account’s email and password.
or with your email
You’re signed in as .
With your account, you can download the zip of every animation and every pack, for free.