Skip to content
animorio
IT
All components

The form that folds into an envelope

Forms, 3D, Transitions, SVGGSAP, CSS only, CanvasAdvancedCompressed weight: Demo 22 KBGSAP 28 KBFreeDownload the zip

The contact form of a stationer written on a sheet of laid letter paper, with its grain, in blue black ink. Mistakes are corrected on the sheet in pencil, as a proofreader would: a loop round an empty line, a wavy line and a caret where the email’s ending is missing, a bracket by a letter too short; once the field is fixed the pencil is rubbed out and leaves a smudge. Sent, the sheet folds in three in 3D with real creases, light and shade on every face, slides into a lined envelope, the flap closes, the sender is written on it, the envelope turns over, takes a stamp and a postmark and flies away; then the clean sheet comes back with Sent. We’ll write back.

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 form that folds into an envelope</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bodoni-moda@5.3.0/opsz.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/bodoni-moda@5.3.0/opsz-italic.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/caveat@5.3.0/index.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/shantell-sans@5.3.0/infm.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the contact form of a stationer, written on a sheet of laid letter paper: a mistake is corrected in pencil
         on the sheet; sent, the sheet folds in three in 3D, slides into a lined envelope, the flap closes, the
         envelope turns over, takes a stamp and a postmark and flies away. Sending is pretended: see post() in the
         script -->
    <div class="post" data-post data-autoclic>
      <div class="desk" data-desk>
        <span class="ground" aria-hidden="true" data-ground></span>

        <form class="sheet" action="#" novalidate aria-labelledby="post-title" data-sheet>
          <div class="paper" data-paper>
            <header class="head">
              <p class="head__from">
                <svg class="head__mark" viewBox="0 0 28 28" aria-hidden="true"><path d="M14 2.5 25.5 14 14 25.5 2.5 14z"/><path d="M11 19.5V8.6h4.1c2.3 0 3.7 1.3 3.7 3.2s-1.4 3.2-3.7 3.2H11"/></svg>
                <span>Quillmere Paper Co.</span>
              </p>
              <h2 class="head__title" id="post-title" tabindex="-1">
                <span class="head__line" data-title-rest>Write to us.</span>
                <span class="head__line head__line--sent" aria-hidden="true" data-title-sent>Sent. We’ll write back.</span>
              </h2>
              <p class="head__sub">
                <span class="head__line" data-sub-rest>Wedding suites, letterheads, a box of samples: every letter is answered by hand.</span>
                <span class="head__line head__line--sent" aria-hidden="true" data-sub-sent>Your letter is on its way to the letter desk. Our answer comes within two days.</span>
              </p>
            </header>

            <div class="field" data-field="name">
              <label class="field__label" for="post-name">Your name</label>
              <div class="field__rule" data-rule>
                <input class="ink" id="post-name" name="name" type="text" autocomplete="name" autocapitalize="words" spellcheck="false" enterkeyhint="next" aria-describedby="post-name-note" aria-invalid="false" data-input>
              </div>
              <p class="note" id="post-name-note" data-note></p>
            </div>

            <div class="field" data-field="email">
              <label class="field__label" for="post-email">Where we write back</label>
              <div class="field__rule" data-rule>
                <input class="ink" id="post-email" name="email" type="text" inputmode="email" autocomplete="email" autocapitalize="off" autocorrect="off" spellcheck="false" enterkeyhint="next" placeholder="you@example.com" aria-describedby="post-email-note" aria-invalid="false" data-input>
              </div>
              <p class="note" id="post-email-note" data-note></p>
            </div>

            <div class="field field--letter" data-field="letter">
              <label class="field__label" for="post-letter">Your letter</label>
              <div class="field__rule field__rule--lines" data-rule>
                <textarea class="ink ink--letter" id="post-letter" name="letter" rows="3" spellcheck="true" aria-describedby="post-letter-note" aria-invalid="false" data-input></textarea>
              </div>
              <p class="note" id="post-letter-note" data-note></p>
            </div>

            <div class="foot">
              <p class="foot__sign">Yours, <span class="foot__name" data-sign></span></p>
              <button class="send" type="submit" data-send>
                <span class="send__bg" aria-hidden="true"></span>
                <span class="send__label">Send the letter</span>
                <svg class="send__icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M3.5 6.5h17v11h-17z"/><path d="m3.8 6.8 8.2 6.4 8.2-6.4"/></svg>
              </button>
            </div>

            <svg class="pencil" aria-hidden="true" data-pencil>
              <defs>
                <filter id="post-graphite" x="-10%" y="-30%" width="120%" height="160%">
                  <feTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="2" seed="7" result="grain"/>
                  <feColorMatrix in="grain" type="matrix" values="0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 -1.5 1.45" result="tooth"/>
                  <feComposite in="SourceGraphic" in2="tooth" operator="in"/>
                </filter>
                <filter id="post-smudge" x="-20%" y="-60%" width="140%" height="220%">
                  <feGaussianBlur stdDeviation="2.4"/>
                </filter>
              </defs>
            </svg>
          </div>
        </form>

        <!-- what the sheet becomes when it is sent: drawn for the eye only, the form itself stays above -->
        <div class="env" aria-hidden="true" data-env>
          <div class="env__turn" data-turn>
            <div class="env__back" data-back>
              <span class="env__wall" data-wall></span>
              <div class="flap" data-flap>
                <span class="flap__in" data-flap-in></span>
                <span class="flap__out" data-flap-out>
                  <span class="flap__from" data-from><span class="flap__who" data-from-name></span><span class="flap__mail" data-from-mail></span></span>
                </span>
              </div>
              <div class="fold" data-fold></div>
              <svg class="pocket" data-pocket></svg>
              <span class="env__shade" data-env-shade></span>
            </div>
            <div class="env__front" data-front>
              <span class="env__border"></span>
              <span class="airmail">By air mail</span>
              <span class="env__to">
                <span class="env__to-desk">The Letter Desk</span>
                <span>Quillmere Paper Co.</span>
                <span>4 Ropewalk Yard</span>
                <span>Quillmere</span>
              </span>
              <span class="stamp" data-stamp>
                <svg viewBox="0 0 60 72" data-stamp-art></svg>
              </span>
              <svg class="cancel" data-cancel></svg>
            </div>
          </div>
        </div>
      </div>
      <p class="sr" role="status" 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.

48 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 form that folds into an envelope” from Animorio and fit it into my site

What it does

On a white background a sheet of cream letter paper, with the fine lines of its laid pattern, the wider chain lines, a grain of specks and fibres and a soft shadow. At the top the mark Quillmere Paper Co. pressed in without ink, then the italic title Write to us. and the line Wedding suites, letterheads, a box of samples: every letter is answered by hand. Below, three fields printed as lines to write on: Your name, Where we write back and Your letter on three lines; whatever is typed comes out in a handwriting in blue black ink. At the bottom Yours, followed by the name being written, like a signature, and the blue button Send the letter with a drawn envelope. A field that is right gets a pencil tick at the end of its line. Leaving a wrong field, or pressing Send the letter, the pencil corrects the sheet: a loop drawn by hand round the empty line, a wavy line under a wrong email with a caret where its ending is missing, a bracket in the margin by a letter too short, and under each field a word written in pencil from left to right (add the ending, like .com; your name, please; a few words, please). Pressing again with the same mistake the pencil goes over its mark once more. As soon as the field is right the mark is rubbed out, a grey smudge stays a moment and the tick comes. When all is right the sheet leans back a little and folds in three: the bottom third rises towards the viewer and folds over the middle, springs open a little as real paper does, and the top third comes down and presses it flat; every face lightens or darkens as it turns to the light, the back shows the writing through, reversed, and a gloss runs along the crease. From below an open envelope rises, lined in green with a gold sprig, the folded letter lifts, drops into the envelope behind its side flaps, the flap closes, the envelope takes the knock and the writer’s name and email are written on the flap. The envelope turns over as it lifts: its front has the red and blue stripes of air mail, the label By air mail and the address The Letter Desk, Quillmere Paper Co., 4 Ropewalk Yard, Quillmere. A stamp with a lighthouse at sunset comes down from above, sticks with a little squash and a bounce, and the roller of the postmark passes from left to right: a round mark with QUILLMERE and today’s date and five wavy lines over the stamp, in uneven ink. After a moment the envelope dips, lifts off and flies away up to the right, turning and shrinking, while a clean sheet settles on the desk with the title Sent. We’ll write back. and the line Your letter is on its way to the letter desk. Our answer comes within two days. As soon as one writes again the title goes back to Write to us.

How it works

A real form under the paper

Two inputs and a textarea, real, with their labels (autocomplete name and email, inputmode email, text at 27 pixels, over the 16 Safari on phones needs not to zoom), aria-invalid and aria-describedby pointing at the pencil’s word under each field, which is real text and is read out. Enter in the name moves to the email, in the email to the letter, Ctrl or Cmd with Enter in the letter sends it. On a wrong send the focus goes to the first field to fix and an aria-live region says what to check; once sent it reads Sent. We’ll write back. and the focus goes to the title. Sending is pretended in one function, post(), the one to replace with the real call.

The proofreader’s pencil

The marks are drawn in SVG over the sheet, measuring where the fields sit with offsetTop and offsetLeft (which ignore transforms) and where the words end with measureText on a canvas, in the field’s own typeface. The loop is a little more than a turn, its radius wavering with two sines and growing a little, so its start and end do not meet; the wavy line, the caret, the bracket and the tick are points with a random but repeatable tremor (a seeded generator), joined by Catmull-Rom curves. Every stroke has pathLength 1 and draws itself with stroke-dashoffset; the graphite is an SVG filter where feTurbulence punches holes in the line. Rubbed out, the strokes move to a group of their own and fade while a wide blurred copy makes the smudge; on resize the marks are drawn again where the fields have gone.

The sheet cut in three, in 3D

On sending, the sheet is copied six times as it is (with what is written on it, without ids, names or aria attributes), and every copy sits inside a face with overflow hidden, shifted by a third: three panels, each with a front and a back, the back turned with rotateX(180) and backface-visibility hidden. The middle panel holds the other two as children in preserve-3d: the lower one turns on its top edge, the upper one on its bottom edge. Every hinge sits half a paper’s thickness above the sheet (translateZ, rotateX, translateZ back), the second a little higher than the first: so, folded, the layers stack in the right order without flicker. The back shows the writing through, upside down and blurred, at five per cent.

The light on the faces

On every frame the normal of every face is worked out: all the rotations are about the same axis, so the angles add up (the tilt of the sheet plus that of the fold) and the normal is (0, −sin a, cos a). Its dot product with the lamp’s direction, less that of the flat sheet, says how much the face lightens or darkens, and a layer over the face takes that warm colour. A fresh fold springs open by RELAX degrees and the second one presses it flat when it lands; while the thirds close, a shadow grows on the middle face, the edge of the fold catches a thread of light and at the end a gloss runs along the crease like a bone folder. Underneath, a shadow on the desk follows the outline of the folded sheet and widens and fades when a third is lifted.

The envelope, in layers

The back of the envelope is a single plane with its layers in z-index order: the lining, the flap, the letter, the side and bottom flaps drawn in SVG (redrawn in pixels when the size changes) and the flap’s shadow. The letter sits inside the envelope from the start, so it really passes between the lining and the flaps; its position is written less that of the envelope. The flap is a flat plane turning with rotateX: past 90 degrees the lining shows, under 90 it goes over the flaps by changing its z-index, and it darkens or lightens with the same light. The front is the other face of a container in preserve-3d turning with rotateY; the stamp has its perforations made with a mask of circles, the postmark a roller that is a mask rectangle widening and an uneven ink made with feTurbulence and feDisplacementMap. The flight is a quadratic Bézier curve; everything is one GSAP timeline calling render() on every frame. A click from a script (detail 0, the preview) makes the whole journey without taking the focus: it writes, gets the email wrong, the pencil corrects it, the hand adds the ending, sends, and at the end the title goes back to rest.

Values to change

NameFileDefaultWhat it changes
FOLDscript.js0.6How many seconds each third of the sheet takes to fold over.
RELAXscript.js7How many degrees a fresh fold springs open before the next third presses it flat: 0 is paper that stays down.
TILTscript.js12How many degrees the sheet leans back while it folds, to show its thickness.
FLAPscript.js0.55How many seconds the envelope’s flap takes to close.
HOLDscript.js0.9How many seconds the stamped envelope stays on the desk before it flies away.
FLIGHTscript.js0.85How many seconds the envelope’s flight lasts.
SETTLEscript.js0.7The seconds of quiet after the last key before a field is read over.
SHORTESTscript.js12The fewest characters the letter may have.
TOWNscript.jsQUILLMEREThe town written round the postmark.
STAMP_VALUEscript.js1.85The value printed on the stamp.

Try them in the Playground

When to use it

On the contact page of anyone working with paper and words: a stationer, a printer, a design studio, a wedding photographer, a small hotel that answers by hand. The envelope leaving is the receipt the writer takes away: they know the message has gone, and to whom. The pencil correction suits any form: it does not shout while you type, waits for the field to be left and goes as soon as it is right. For another brand change the colours, the address, the stamp and the postmark.

On phones

The sheet takes the width of the screen (358 pixels on a 390 phone) and the envelope 94 per cent of the sheet, so nothing sticks out at the sides; the flight goes upwards and the envelope is gone before the edge. The fields are 40 pixels tall and the letter 108, the button 50, all over the 44 of a finger; the text is at 27 pixels, so Safari does not zoom the page on a tap; the email opens the keyboard with the @ and the Enter key says Next. The button, which on a computer swells under the pointer and changes the cut of its letters, squashes under a tap. The same folds, the same envelope and the same flight, at the phone’s size.

With reduced motion

No folds, no flight and no strokes running: the pencil marks and the words under the fields fade in and out. On sending the sheet fades into the envelope already closed and turned over, then the stamp and the postmark fade in; after the same moment of waiting the envelope fades away and the clean sheet fades back with Sent. We’ll write back. The title changes by crossing over, without moving.

Pitfalls

  • The envelope’s flap was a container in preserve-3d with its two faces turned, inside the back of the envelope, which is flat to keep the layers in z-index order: in Chrome the open flap vanished completely and showed whole just when it was edge on. Now the flap is a single plane and the script decides which face to show: past 90 degrees the lining, under it the outside of the envelope.
  • The back of the lower third, lifted towards the viewer, faces downwards and with the computed light turned almost brown: paper never looks like that. The darkening now has a low ceiling and a warm colour, and the sheet stays paper even against the light.
  • Two panels folded to 180 degrees lie in the same plane as the middle panel and flickered over each other. The hinges sit half a paper’s thickness above the sheet, the second higher than the first, so once folded they really stack.
  • The envelope came up from below while it faded in: for a moment the half transparent flaps let the letter show behind them. Now it is solid within a tenth of a second, while it is still far from the letter.
  • The shadow on the desk was a transparent box with a box-shadow: when the envelope flew away it stayed on the desk like a pale card. Now as soon as the envelope lifts off the shadow softens and goes.
  • Pencil marks being rubbed out vanished at once when a new one was drawn straight after, because the new drawing emptied the field’s group. The strokes to rub out now move to a group of their own and fade there.
  • The tick of a field that is right, at the end of its line, landed on the end of a long letter: it now sits in the pencil’s row under the field, where it never covers the writing.
  • The graphite filter and the postmark’s ink filter punched too many holes and the strokes looked like rows of dots: the noise is wider and the holes rarer, and the town on the postmark, unreadable before, is bigger.
  • In the first version the preview’s round lasted over eighteen seconds: the sample letter is shorter, the pause on the mistake briefer and the stamped envelope waits a little under a second.

Libraries

GSAP 3.15.0, Bodoni Moda (font variabile) 5.3.0, Caveat (font variabile) 5.3.0, Shantell Sans (font variabile) 5.3.0.

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

Testing

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