
The holographic card
Cards, 3D, Hover
Free
The footer of a small tea house, made all of a raked sand garden lit by a low sun, with stones, moss and rings raked round them, and the house's name pressed into the sand along the bottom. The pointer is a rake that carves grooves where it goes, a link pointed at gets a ring raked round its word, and signing up to the letters brings a wide rake across that leaves the garden as new.
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 zen garden footer</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/newsreader@5.3.0/opsz.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/newsreader@5.3.0/opsz-italic.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="stage">
<!-- the footer of a small tea house: its whole ground is a raked sand garden lit by a low sun, with stones and
moss and rings raked round them, and the house's name pressed into the sand along the bottom. The pointer is
a rake that carves grooves where it goes, a link pointed at gets a ring raked round its word, and a sign-up
to the letters brings a wide rake across that leaves the garden as new -->
<footer class="zg" data-footer data-autoclic>
<canvas class="zg__sand" aria-hidden="true" data-sand></canvas>
<canvas class="zg__still" aria-hidden="true" data-still></canvas>
<canvas class="zg__rake" aria-hidden="true" data-rake></canvas>
<div class="zg__top">
<form class="zg__form" action="#" novalidate aria-labelledby="zg-news" data-form>
<h2 class="zg__title" id="zg-news" data-in>Letters from the tea house</h2>
<p class="zg__lede" data-in>New harvests, tasting evenings and the garden through the year. One letter each season.</p>
<label class="sr" for="zg-email">Email address</label>
<div class="zg__field" data-field data-in>
<input class="zg__input" id="zg-email" name="email" type="email" inputmode="email" autocomplete="email" autocapitalize="off" spellcheck="false" enterkeyhint="send" placeholder="you@example.com" aria-describedby="zg-note" aria-invalid="false" data-input>
<button class="zg__send" type="submit" data-send>
<span class="zg__send-bg"></span>
<span class="zg__send-label" data-send-label>Subscribe</span>
<svg class="zg__spin" viewBox="0 0 24 24" aria-hidden="true" data-spin><circle cx="12" cy="12" r="8"/></svg>
<svg class="zg__done" viewBox="0 0 24 24" aria-hidden="true" data-done><path d="M6.4 12.6l3.7 3.7 7.6-8.6"/></svg>
</button>
</div>
<p class="zg__note" id="zg-note" aria-live="polite" data-note data-in><span class="zg__line">Four letters a year. Leave with one click.</span></p>
</form>
<!-- where the stones lie: the script reads these boxes, the stones are drawn in the sand -->
<div class="zg__garden" aria-hidden="true">
<span class="zg__stone zg__stone--big" data-stone></span>
<span class="zg__stone zg__stone--small" data-stone></span>
</div>
<nav class="zg__cols" aria-label="Footer">
<div class="zg__col" data-in>
<h3 class="zg__head">Visit</h3>
<ul>
<li><a class="zg__link" href="#" data-link>Opening hours</a></li>
<li><a class="zg__link" href="#" data-link>Find us</a></li>
<li><a class="zg__link" href="#" data-link>Private rooms</a></li>
<li><a class="zg__link" href="#" data-link>Gift cards</a></li>
</ul>
</div>
<div class="zg__col" data-in>
<h3 class="zg__head">Tea</h3>
<ul>
<li><a class="zg__link" href="#" data-link>Our teas</a></li>
<li><a class="zg__link" href="#" data-link>Tastings</a></li>
<li><a class="zg__link" href="#" data-link>Tea ware</a></li>
<li><a class="zg__link" href="#" data-link>Wholesale</a></li>
</ul>
</div>
<div class="zg__col" data-in>
<h3 class="zg__head">House</h3>
<ul>
<li><a class="zg__link" href="#" data-link>Our story</a></li>
<li><a class="zg__link" href="#" data-link>The garden</a></li>
<li><a class="zg__link" href="#" data-link>Journal</a></li>
<li><a class="zg__link" href="#" data-link>Contact</a></li>
</ul>
</div>
</nav>
</div>
<div class="zg__base" data-in>
<p class="zg__brand">
<svg class="zg__mark" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 22V3.2"/><path d="M12 6.4L8.6 9.6M12 6.4l3.4 3.2"/><path d="M12 10.6l-5 4.4M12 10.6l5 4.4"/><path d="M12 15l-6.6 5.2M12 15l6.6 5.2"/></svg>
<span class="zg__copy"><svg viewBox="0 0 12 12" aria-hidden="true"><circle cx="6" cy="6" r="5.1"/><path d="M7.9 4.5a2.3 2.3 0 1 0 0 3"/></svg><span class="sr">Copyright</span> 2026 Hollow Pine Tea House</span>
<span class="zg__hours">Open Wednesday to Sunday, 10 till 6</span>
</p>
<nav class="zg__legal" aria-label="Legal">
<a class="zg__link zg__link--small" href="#" data-link>Privacy</a>
<a class="zg__link zg__link--small" href="#" data-link>Terms</a>
<a class="zg__link zg__link--small" href="#" data-link>Cookies</a>
</nav>
</div>
<!-- the house's name: real text, pressed into the sand by the script where the text stands -->
<div class="zg__foot">
<p class="zg__name" data-name><span data-word>Hollow</span> <span data-word>Pine</span></p>
<span class="zg__stone zg__stone--corner" aria-hidden="true" data-stone></span>
</div>
</footer>
</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.
61 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 zen garden footer” from Animorio and fit it into my siteThe sand is a height every CELL pixels, drawn on a canvas of that size which the browser enlarges smoothly. For every point the slope comes from the differences with its neighbours, and a low sun (SUN and SUN_HEIGHT) lights it the Lambert way: the light it gets, divided by what flat ground gets, picks among three sand colours (flat, in the sun, in shade), a hollow gathers a little shade (the Laplacian) and the light warms slightly on the side of the sun. The grain is a noise in every point's height plus a fine dust of light and dark specks drawn once at the screen's own pixels.
Worked out once: straight lines with a slow wave and a depth that varies a little, as raked by hand; rings round the stones measured from the edge of the moss along the ray from the stone's centre, rounder the further out they go, with the lines ending on the outermost crest. The name is drawn in its own font on a hidden canvas at twice the size, exactly where the real words stand (each word's box plus fontBoundingBoxAscent); a blur makes the walls of the stamp, the floor is flat and a little darker, and round it the sand is smoothed and lifted.
The rake's head only moves when the pointer is further than HANDLE pixels away, and then straight towards it: so it trails behind and swings round to follow, and the teeth always lie across the way. Between two pointer positions it goes in steps of 0.7 points, so a quick gesture misses nothing. At every step the sand within the rake's width takes the teeth's profile (a cosine with round troughs and narrower crests), the outer teeth heap a crest and just outside the sand pushed aside rises; there is no digging where the stones and moss are. Only the rectangle touched is lit again, with putImageData's dirty rectangle.
A ring is not written into the sand: while it is lit, its band (two teeth) takes the place of the sand under it, as far round as GSAP has drawn it (the angle counted from the left end, with a soft tip), and inside the oval the sand is smoothed. The distance from the oval comes from the ellipse's equation divided by its slope. Moving away, its strength goes down to zero and the sand under it shows again as it was, grooves and all.
After a sign-up a GSAP tween takes the front from left to right in SWEEP seconds: every column it passes takes back the freshly raked sand, and a small heap of pushed sand swells ahead of the bar. The bar with its teeth and the small rake are drawn on a third canvas, with their shadow. Stones, moss and shadows are drawn once, pixel by pixel, at the screen's resolution: a boulder flat on top and round at the shoulders, cracks from a ridged noise, the granite's speckle, a hint of sheen, a cushion of moss made of small domes and moss climbing the shady side; the cast shadow is the outline drawn off the canvas with its blurred shadow brought back in.
| Name | File | Default | What it changes |
|---|---|---|---|
TINES | script.js | 5 | How many teeth the rake has: as many grooves as it leaves on each pass. |
TINE_GAP | script.js | 9 | How far apart the teeth are, in pixels: one groove and one crest. It goes for the garden's lines and rings too. |
DEPTH | script.js | 1.2 | How deep the teeth dig, in pixels of sand: more makes sharper grooves, with more light and more shade. |
PRESS | script.js | 2.6 | How deep the name along the bottom is pressed into the sand, in pixels: at 0 it is gone. |
SUN | script.js | 125 | Where the sun comes from, in degrees round the footer: 90 from the top, 180 from the left. From straight left the level lines nearly vanish. |
SUN_HEIGHT | script.js | 24 | How high the sun stands, in degrees: lower makes the grooves stand out more and the stones' shadows longer. |
GRAIN | script.js | 0.6 | How much grain the sand shows: at 0 it is as smooth as plaster. |
RINGS | script.js | 3 | How many rings are raked round each stone. |
CELL | script.js | 2 | How many pixels each point of the sand covers: more is less work and softer sand, less is more detail. |
HANDLE | script.js | 34 | How far the rake's head trails behind the pointer, in pixels: more makes wider, softer curves. |
SWEEP | script.js | 1.9 | How long the wide rake takes to cross the footer after a sign-up, in seconds. |
WAIT | script.js | 1.1 | How long the sign-up takes in the demo, in seconds. |
For the footer of a tea house, a spa, a meditation retreat, an app for calm or sleep, a bonsai nursery, a potter or a landscape architecture studio: anywhere the bottom of the page can be a place to stop for a moment and play with the sand.
The footer takes the width of the screen and becomes a taller column: the newsletter, then a strip of open sand with the two stones and their rings, then the three columns of links, the mark, the opening hours and the legal links, and the name pressed in on two lines, Hollow and Pine, with the third stone beside Pine. The sand has fewer points but the same grain. A finger dragged sideways rakes (up and down scrolls the page), and the rake shows while the finger is down; tapping a link draws the ring round its word and it stays a moment.
Nothing passes by itself: the garden is already raked when the footer appears, fading in. The rake stays, since the hand moves it. A link pointed at has its ring already closed, fading in with the smooth pool; a wrong field does not shake, the button does not narrow and the arc keeps still; on a sign-up the wide rake does not cross and the garden fades back to new where it is.
GSAP 3.15.0, Newsreader (font variabile) 5.3.0, Mona Sans (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 23 KB; the libraries: GSAP 28 KB.
Tested on 7 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.