
The trapdoor table
Tables, Physics, SVG, Transitions, 3D
NewFree
A model export made as a small 3D printer seen from the front: press Export model and the plate comes up to the nozzle and the printer builds a twisted vase layer after layer. The nozzle runs along the layer, the fresh plastic is orange and cools through red and purple into the filament’s blue, the plate drops a layer at every lap and the spool turns. The vase is the progress; at the end the head parks, the plate comes down to you and a glint runs across the vase.
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, viewport-fit=cover">
<title>The 3D printer loader</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/instrument-sans@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the 3D printer loader: the export of a model from a 3D modelling app, shown as a small printer seen from the
front and a little from above. Pressed, the plate comes up to the nozzle, a purge line is laid along its
front edge and a twisted vase is printed layer after layer: the nozzle runs round the current layer, the
fresh plastic glows orange behind it and cools into the colour of the filament, the plate goes down a layer
at every lap and the spool turns a little. The vase is the progress. The printer is drawn by script.js -->
<section class="printer" aria-label="Model export" data-printer>
<!-- four layers: the frame's back and the plate; the vase; the gantry and the print head; the frame's front,
the spool and the tube -->
<div class="printer__scene" role="progressbar" aria-label="Model export" aria-valuemin="0" aria-valuemax="100"
aria-valuenow="0" aria-valuetext="Ready to export" data-scene>
<svg class="printer__art" viewBox="0 6 300 256" aria-hidden="true" data-art="back"></svg>
<canvas class="printer__art" aria-hidden="true" data-paint></canvas>
<svg class="printer__art" viewBox="0 6 300 256" aria-hidden="true" data-art="head"></svg>
<svg class="printer__art" viewBox="0 6 300 256" aria-hidden="true" data-art="front"></svg>
</div>
<p class="printer__status" aria-hidden="true">
<svg class="printer__icon" viewBox="0 0 24 24" data-icon><path d="M5.5 12.6l4.2 4.2 8.8-9.4" pathLength="1"/></svg>
<span class="printer__swap" data-status>Ready to export</span>
<span class="printer__pct" data-pct></span>
<span class="printer__left" data-left></span>
</p>
<div class="printer__buttons">
<button class="pill is-quiet" type="button" hidden data-again><span class="pill__label">Export again</span></button>
<button class="pill" type="button" data-go data-autoclic><span class="pill__label" data-label>Export model</span></button>
</div>
<p class="sr" role="status" 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>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.
47 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 3D printer loader” from Animorio and fit it into my siteEvery point of the printer has three coordinates (x to the right, y towards you, z up) and lands in the drawing at x, −z·cos e + y·sin e, with sin e = 0.24: the printer is seen from 14 degrees above, with no perspective. So the plate is a squashed rectangle, the top of the frame a ring and the nozzle’s lap an ellipse. The frame, the plate, the head and the spool are SVG drawn once only; every frame the plate, the beam and the head move, the fan turns (and at speed becomes a blurred disc), the extruder’s gear turns, the tube is drawn again as a Bézier curve down to the head and the filament inside it runs with a dash pattern.
The vase’s profile is a smooth curve (cubic Hermite) through the points of PROFILE and gives the radius R at every height. The outline at every height is r(θ) = R·(1 + d·cos(n·(θ − φ))), with n ribs of depth d, and φ growing with the height up to TWIST: the ribs turn as they rise. Every layer is a band from the outline at its bottom to the outline at its top, so the side of the vase runs on without steps and the layers, 2.5 units tall (under 3 pixels), are left as a fine texture. The two bottom layers are solid: lines at 45 degrees, then the other way, clipped to the outline by intersecting a line with a 96 sided polygon.
The path is a list of real moves: the plate coming up, the priming, the purge line and then, before every travel, the retraction, the plate dropping a little more (the hop), the travel, coming back down and the filament pushed back; in the bottom the zigzag, and the first layer at half speed so it sticks to the plate. Every lap starts at the back, where the twist has brought it, and there the plate drops a layer while the head slides a few degrees: no travels between one lap and the next. The fixed moves keep their time, the printing ones share what is left of SECONDS by their length. From there come the place of the head and of the plate at every moment, the percentage (the time done over the path’s time) and the time left.
Every lap is cut into 120 slices. For each one the surface’s normal comes from the radius, its derivative along the lap (the ribs) and the slope of the profile; the light is diffuse (Lambert), darker towards the edges, with a narrow upright shine that follows the ribs. The light wraps a little round the ribs, so their shaded side is soft. It is painted in two passes: first the bottom and the inside of the back wall, seen through the opening, then the front walls from the bottom up and the rim on top. Over each bead a line of light, stronger on the lit side, under it a light shade and the fine crease where it sits on the layer below: those are the raised layer lines.
Every slice knows the moment the nozzle laid it, so its heat is T = e^(−age ÷ COOL). The colour follows a thermal camera’s scale: nearly white, orange, red, purple and the filament’s colour; the hotter it is the less it is shaded, because it glows by itself. Along the hot part of the last two layers an orange glow is added (lighter). When the print is canceled the path stops but the clock does not, and the piece left on the plate cools all the same.
A finished, cold layer is added once only to two pictures of their own (the two passes); the plate moves in whole pixels of the canvas, so the pictures follow it without blurring, and every frame only the layers still warm are painted. When nothing moves and nothing is hot, the loop does not draw. The drawing is a role=progressbar with aria-valuenow and aria-valuetext («54 percent, about 6 seconds left») at most every 0.6 seconds, aria-busy while it prints and an aria-live line for the start, the cancel, the end and the download. Clicks from a script (the library preview’s) never cancel or download: once the print is done, after 2.5 seconds, they start it again.
| Name | File | Default | What it changes |
|---|---|---|---|
SECONDS | script.js | 7 | How long the demo’s print takes, in seconds, from the plate coming up to the last lap (a real export follows the app’s work). |
LAYERS | script.js | 36 | The vase’s layers, the two solid ones at the bottom included: more layers, finer lines and more laps of the nozzle, which goes faster. |
PROFILE | script.js | [[0, 19], [0.14, 24], [0.4, 32], …] | The vase’s profile: for every point at what height (from 0, the foot, to 1, the rim) and the radius in drawing units (the plate is 148 wide). |
RIBS | script.js | 8 | The vase’s ribs; 0 makes it smooth. |
RIB_DEPTH | script.js | 0.04 | How far the ribs stand out, as a share of the radius. |
TWIST | script.js | 80 | How many degrees the ribs turn from the foot to the rim; a negative number turns them the other way. |
COOL | script.js | 0.35 | In how many seconds the fresh plastic loses nearly two thirds of its heat: the higher it is, the more layers stay lit under the nozzle. |
LABELS | script.js | { busy: 'Cancel', done: 'Download .stl', saved: 'Downloaded', retry: 'Try again' } | The button’s words while it prints, at the end, once downloaded and after a cancel; the one at rest, Export model, is changed in index.html. |
WORDS | script.js | { warm: 'Warming up', busy: 'Exporting model', done: 'Model ready', canceled: 'Canceled at' } | The line under the printer while it warms up, while it prints (followed by the percentage and the time left), at the end and after a cancel (followed by the percentage); the one at rest, Ready to export, is changed in index.html. |
For a wait of a few seconds that ends with an object in hand: exporting a model from a 3D modelling or CAD app, generating a mesh or an object with AI, preparing an STL or 3MF file, a slicer’s work. It suits a product for makers, product designers or 3D printing studios, where the printer says at once what this is about and the vase says how far along things are even without reading the number. For a very short wait, or one that produces nothing to keep, it is too much.
The drawing is 330 pixels wide and fits whole on a phone (at 390, 16 pixels are left on each side); it is vector, so it stays sharp at any density, and the vase’s canvas paints at the screen’s real density, up to twice. The status stays on one line. The buttons are pills 44 tall: under a finger they squash and spring back; the pill that swells, the words that widen and the head that leans out on the way past are there only with the mouse (the head also with the keyboard).
The head stays in its corner and the plate still at its end of print position; the fan, the extruder’s gear and the spool do not turn, no glow and no glint at the end. The vase grows all the same, because it is what says how far along things are: it comes in six steps, each fading in over the one before and always with a whole lap on top, already in the filament’s colour. The percentage and the time left update, the words change with a fade and the tick appears without drawing itself.
GSAP 3.15.0, Instrument 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.