
The vacuum-sealed chat
Chat, Physics, SVG, Transitions, Text
NewFree
The booking form of a pottery’s wheel class, made of clay: a field that is right gets a coat of raw glaze brushed over it, one that is wrong turns iron red and wobbles like a pot off centre. Sent, it goes in the kiln: in a little over two seconds it glows by itself dull red, cherry, orange and yellow, in the true colours of a body that hot computed with Planck’s law, shrinks by twelve per cent like clay and comes out a glazed tile in the glaze chosen, with the booking pressed into 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 form that goes in the kiln</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/soft.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bricolage-grotesque@5.3.0/wdth.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the booking form of a pottery's wheel class, made of clay: a field that is right gets a coat of raw glaze
brushed over it, one that is wrong turns iron red; sent, the slab goes in the kiln, glows from dull red to
yellow white, shrinks like clay and comes out a glazed tile in the glaze chosen. The booking is pretended:
see book() in the script -->
<form class="kiln" action="#" novalidate aria-labelledby="kiln-title" data-kiln data-autoclic>
<div class="slab" data-slab>
<div class="face" data-face>
<div class="head">
<h2 class="head__title" id="kiln-title">Thursday wheel class</h2>
<p class="head__sub">Six evenings on the wheel, 7 to 9 pm. Clay, tools and two firings included.</p>
</div>
<div class="field" data-field="name">
<label class="field__label" for="kiln-name">Your name</label>
<div class="well" data-well>
<svg class="coat" aria-hidden="true" data-coat></svg>
<input class="well__input" id="kiln-name" name="name" type="text" autocomplete="name" autocapitalize="words" spellcheck="false" enterkeyhint="next" aria-describedby="kiln-name-msg" aria-invalid="false" data-input>
<span class="well__caret" aria-hidden="true" data-caret></span>
<svg class="tick" viewBox="0 0 20 20" aria-hidden="true" data-tick><path d="M4.8 10.6l3.4 3.4 7-7.6"/></svg>
</div>
<div class="field__msg" id="kiln-name-msg" data-msg></div>
</div>
<div class="field" data-field="email">
<label class="field__label" for="kiln-email">Email</label>
<div class="well" data-well>
<svg class="coat" aria-hidden="true" data-coat></svg>
<input class="well__input" id="kiln-email" name="email" type="text" inputmode="email" autocomplete="email" autocapitalize="off" autocorrect="off" spellcheck="false" enterkeyhint="send" placeholder="you@example.com" aria-describedby="kiln-email-msg" aria-invalid="false" data-input>
<span class="well__caret" aria-hidden="true" data-caret></span>
<svg class="tick" viewBox="0 0 20 20" aria-hidden="true" data-tick><path d="M4.8 10.6l3.4 3.4 7-7.6"/></svg>
</div>
<div class="field__msg" id="kiln-email-msg" data-msg></div>
</div>
<fieldset class="field field--picks" role="radiogroup" aria-labelledby="kiln-date-legend" aria-describedby="kiln-date-msg" aria-required="true" aria-invalid="false" data-field="date">
<legend class="field__label" id="kiln-date-legend">First Thursday</legend>
<div class="picks">
<label class="pick">
<input type="radio" name="date" value="Thursday 15 October" data-input>
<span class="pick__body"><svg class="coat" aria-hidden="true" data-coat></svg><span class="pick__main">15 Oct</span><span class="pick__sub">4 seats left</span><svg class="tick tick--pick" viewBox="0 0 20 20" aria-hidden="true" data-tick><path d="M4.8 10.6l3.4 3.4 7-7.6"/></svg></span>
</label>
<label class="pick">
<input type="radio" name="date" value="Thursday 5 November" disabled data-input>
<span class="pick__body"><span class="pick__main">5 Nov</span><span class="pick__sub">Full</span></span>
</label>
<label class="pick">
<input type="radio" name="date" value="Thursday 26 November" data-input>
<span class="pick__body"><svg class="coat" aria-hidden="true" data-coat></svg><span class="pick__main">26 Nov</span><span class="pick__sub">9 seats left</span><svg class="tick tick--pick" viewBox="0 0 20 20" aria-hidden="true" data-tick><path d="M4.8 10.6l3.4 3.4 7-7.6"/></svg></span>
</label>
</div>
<div class="field__msg" id="kiln-date-msg" data-msg></div>
</fieldset>
<fieldset class="field field--picks" role="radiogroup" aria-labelledby="kiln-glaze-legend" aria-describedby="kiln-glaze-msg" aria-required="true" aria-invalid="false" data-field="glaze">
<legend class="field__label" id="kiln-glaze-legend">Glaze for your first bowl</legend>
<div class="picks">
<label class="pick">
<input type="radio" name="glaze" value="celadon" data-input>
<span class="pick__body"><svg class="coat" aria-hidden="true" data-coat></svg><svg class="bowl" viewBox="0 0 40 20" aria-hidden="true"><path class="bowl__glaze bowl--celadon" d="M2 3.5h36c0 8.6-7.4 14-18 14S2 12.1 2 3.5z"/><path class="bowl__shine" d="M7.5 7.2c2.2 3.6 6 5.4 10.4 5.8"/></svg><span class="pick__main">Celadon</span><svg class="tick tick--pick" viewBox="0 0 20 20" aria-hidden="true" data-tick><path d="M4.8 10.6l3.4 3.4 7-7.6"/></svg></span>
</label>
<label class="pick">
<input type="radio" name="glaze" value="tenmoku" data-input>
<span class="pick__body"><svg class="coat" aria-hidden="true" data-coat></svg><svg class="bowl" viewBox="0 0 40 20" aria-hidden="true"><path class="bowl__glaze bowl--tenmoku" d="M2 3.5h36c0 8.6-7.4 14-18 14S2 12.1 2 3.5z"/><path class="bowl__shine" d="M7.5 7.2c2.2 3.6 6 5.4 10.4 5.8"/></svg><span class="pick__main">Tenmoku</span><svg class="tick tick--pick" viewBox="0 0 20 20" aria-hidden="true" data-tick><path d="M4.8 10.6l3.4 3.4 7-7.6"/></svg></span>
</label>
<label class="pick">
<input type="radio" name="glaze" value="shino" data-input>
<span class="pick__body"><svg class="coat" aria-hidden="true" data-coat></svg><svg class="bowl" viewBox="0 0 40 20" aria-hidden="true"><path class="bowl__glaze bowl--shino" d="M2 3.5h36c0 8.6-7.4 14-18 14S2 12.1 2 3.5z"/><path class="bowl__shine" d="M7.5 7.2c2.2 3.6 6 5.4 10.4 5.8"/></svg><span class="pick__main">Shino</span><svg class="tick tick--pick" viewBox="0 0 20 20" aria-hidden="true" data-tick><path d="M4.8 10.6l3.4 3.4 7-7.6"/></svg></span>
</label>
</div>
<div class="field__msg" id="kiln-glaze-msg" data-msg></div>
</fieldset>
<button class="fire" type="submit" data-fire>
<span class="fire__bg" aria-hidden="true"></span>
<span class="fire__label" data-fire-label>Book my seat · €240</span>
<span class="fire__heat" aria-hidden="true" data-heat></span>
</button>
</div>
<!-- what comes out of the kiln -->
<section class="tile" aria-labelledby="kiln-tile-title" hidden data-tile>
<span class="tile__shine" aria-hidden="true" data-shine></span>
<div class="tile__top">
<p class="tile__kicker"><svg viewBox="0 0 20 20" aria-hidden="true"><path d="M4.8 10.6l3.4 3.4 7-7.6"/></svg>Seat booked</p>
<h2 class="tile__title" id="kiln-tile-title" tabindex="-1" data-tile-title>See you at the wheel.</h2>
</div>
<dl class="tile__facts">
<div><dt>First class</dt><dd data-out="date"></dd></div>
<div><dt>Your bowl’s glaze</dt><dd data-out="glaze"></dd></div>
<div><dt>Details sent to</dt><dd data-out="email"></dd></div>
</dl>
<p class="tile__note">Bring an apron and short nails. Your bowl comes out of its last firing three weeks later.</p>
<button class="again" type="button" data-again><span class="again__bg" aria-hidden="true"></span><span class="again__label">Book another seat</span></button>
</section>
<span class="slab__dark" aria-hidden="true" data-dark></span>
<span class="slab__glow" aria-hidden="true" data-glow></span>
<span class="slab__veil" aria-hidden="true" data-veil></span>
<span class="slab__flame" aria-hidden="true" data-flame></span>
</div>
<p class="sr" role="status" aria-live="polite" data-live></p>
</form>
</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.
49 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 form that goes in the kiln” from Animorio and fit it into my siteTwo real inputs with their labels (autocomplete name and email, inputmode email, text at 16 pixels so Safari on phones does not zoom) and two groups of real radios in a fieldset with role radiogroup: the tiles are the radios themselves, invisible over their drawing, so arrows, space, taps and screen readers work by themselves and the Full tile is really disabled. Every field has aria-invalid and aria-describedby pointing at its line underneath; on sending the focus goes to the first wrong field and an aria-live region says what to check, then reads the booking out.
Every field has three states: at rest, right, wrong. Typing, SETTLE seconds after the last key (a GSAP delayedCall restarted at every key) a field that has become right takes the glaze; one that is not right yet is left alone until it is left, and only then turns red. A red field is read at every key: it is right again as soon as it is, while the words of its error only change after a pause, so they do not flicker between one key and the next. The line underneath comes in with its height animated to auto.
The glaze is an SVG as big as the well, rebuilt when its size changes: a strip every five or six pixels, with round ends like bristles. On every frame each strip reaches p·(width + 40) less its own delay, taken from two sines out of phase: the edge of the wet glaze is ragged while it runs and straight once it has arrived. Each strip has an opacity of its own, which leaves the streaks of the brush. The tick draws itself with stroke-dashoffset when the brush is halfway.
At the start a table is computed every 10 degrees: Planck’s law from 380 to 780 nanometres, weighed by the CIE 1931 curves (the analytic fit of Wyman, Sloan and Shirley), taken to linear sRGB and normalised. Then it is looked at as through a spy hole: an exposure that grows with the luminance, and each channel saturating on its own (1 − e^(−c·E)), plus a little glare at the top; so under 525 degrees nothing shows, at 700 it is cherry and at the top the saturated red lets the green climb to a light yellow. The glow is a layer in mix-blend-mode screen over the slab already darkened, that is light that adds up, with a radial gradient between the edge and a middle that has a temperature of its own and chases it late. Near the top a layer of light patches in screen drifts slowly and breathes with two sines, because a kiln’s light is never a flat colour.
One GSAP timeline, a little over two seconds: the surroundings go dark, the temperature climbs in HEAT seconds (the dark part quickly, then red, orange and yellow a little faster towards the top) and meanwhile the slab shrinks by SHRINK with a scale on the whole of it, like clay: the tile is laid out as wide and as tall as the form, so nothing reflows. That is why at the top a veil the colour of the glowing clay is enough for a tenth of a second, while under it the form becomes the tile; after SOAK seconds the temperature falls in COOL seconds, fast at first like anything hot, and the door opens again at the last red: the tile can be read. The highlight and the last degrees come after. With reduced motion the slab keeps its size and only the colours dissolve. A click from a script (detail 0, the preview) makes one move at a time without taking the focus, with a drawn caret: the first fills the form in right (the catalogue’s picture), the second sends it to the kiln; in the rounds after, a new name, the wrong email, the fix and the choices, with another glaze.
| Name | File | Default | What it changes |
|---|---|---|---|
PEAK | script.js | 1240 | The temperature at the top of the firing, in degrees: 1240 is stoneware; a lower firing stays redder. |
HEAT | script.js | 1.1 | How many seconds the kiln takes to climb to the top. |
SOAK | script.js | 0.2 | How many seconds the kiln holds the top: the form becomes the tile in the first tenth. |
COOL | script.js | 1.4 | How many seconds it takes to cool down: the tile can be read after a third of it. |
SHRINK | script.js | 0.12 | How much the slab shrinks in the kiln, like clay: 0.12 is twelve per cent. |
BRUSH | script.js | 0.6 | How many seconds the brush takes to glaze a field that is right. |
SETTLE | script.js | 0.6 | The seconds of quiet after the last key before the field reads itself over. |
When booking a class, a workshop or a visit, where the form is short and the confirmation should stay in mind: the change on sending is the moment the person signing up knows it is done. The checking suits any form: it waits for the typing to stop, does not shout while you type and goes as soon as the field is right. The kiln and the glaze are the theme of a pottery; for another product keep the brush and the change under the veil and change the colours.
The slab takes the width of the screen (358 pixels on a 390 phone) and is about as tall as the screen; the fields are 48 tall, the tiles 56 and 64, the button 52, all over the 44 of a finger. The fields’ text is at 16 pixels, so Safari does not zoom the page on a tap; the email opens the keyboard with the @; the Enter key says Next in the name and moves on to the email, and Send in the email. The tiles are chosen with a tap and the hover only applies where there is a pointer. The tile comes out 315 wide and its lines wrap by themselves.
The brush does not run: the glaze and the tick fade in. Wrong fields do not wobble, the lines underneath swap with a fade and the button’s words cross without moving. In the kiln only the colours stay, with the same timing: the dark, the glow and the veil dissolve into one another, the slab does not shrink, the patches of light stay still and the highlight on the tile does not run. Book another seat dissolves the tile into the new form.
GSAP 3.15.0, Fraunces (font variabile) 5.3.0, Bricolage Grotesque (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 17 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.