Skip to content
animorio
IT
All components

The ship in a bottle

Loading, Physics, TransitionsCanvas, GSAPAdvancedCompressed weight: Demo 20 KBGSAP 28 KBFreeDownload the zip

An app install as a ship inside a glass bottle lying on its side: the hull is already in, its masts laid flat on the deck, and as the files are copied a thread that runs out of the neck pulls them up one after another, each on a sprung hinge. Then the sails unroll and fill with wind, the pennants run up the mastheads, the thread is cut and the cork knocks into the neck: installed. If it goes wrong, the mast that was going up falls back onto the deck; Retry raises it again.

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, viewport-fit=cover">
  <title>The ship in a bottle installer</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the ship in a bottle: the install of an app shown as a schooner inside a glass bottle lying on a wooden stand.
         The hull is already in, sitting in a sea of blue resin, with its three masts laid flat on the deck. As the files
         are copied a thread that runs out of the bottle's neck to a little winch pulls the masts up one after another,
         each on a sprung hinge; then the sails unroll and fill with wind and the pennants stream out. At the end the
         thread is cut and the cork hops into the neck. Everything is painted by script.js on the canvas -->
    <section class="ship" aria-label="App installer" data-veliero>
      <div class="ship__scene" role="progressbar" aria-label="Install progress" aria-valuemin="0" aria-valuemax="100"
           aria-valuenow="0" aria-valuetext="Ready to install" data-scene>
        <canvas class="ship__paint" aria-hidden="true" data-paint></canvas>
      </div>
      <div class="ship__foot">
        <div class="ship__words" aria-hidden="true">
          <p class="ship__title"><span class="ship__swap" data-title>Harbor 3.0</span></p>
          <p class="ship__meta"><span class="ship__swap" data-meta>Ready to install · 84.2 MB</span></p>
        </div>
        <div class="ship__buttons">
          <button class="pill is-quiet" type="button" hidden data-again><span class="pill__label">Reinstall</span></button>
          <button class="pill" type="button" data-go data-autoclic><span class="pill__label" data-label>Install</span></button>
        </div>
      </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>

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.

47 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 ship in a bottle” from Animorio and fit it into my site

What it does

On a black background, alone in the middle, a pale green glass bottle lying on a wooden stand with two cradles; inside, a three masted schooner on a sea of blue resin sculpted with a wave, white foam at the bow and the stern, the masts laid flat on the deck with their gaffs and booms folded along them. A thread comes out of the neck and runs down to a little winch with a crank, and next to it the cork lies on the plank. Under the bottle Harbor 3.0, Ready to install · 84.2 MB and the Install pill. Pointing at the pill takes up the thread and the masts lift a little off the deck. Pressed, the winch starts to turn and the thread pulls up the foremast, then the mainmast, then the mizzen: each turns on its hinge, its boom and gaff swing open halfway up, and once upright the mast rings a few times on its spring before it stops. Meanwhile the sea moves a little and the ship pitches. Then the jib unrolls off its stay and the three sails come down from their gaffs like blinds, each roll thinning as it goes, and as soon as they are out the wind fills them: the light on the cloth shows the belly of the sail. At the end the red pennants run up the mastheads and stream out. Below, Installing Harbor 3.0 and a line that says what is going on (Copying files, Configuring, Finishing up) with the percentage; the pill becomes Cancel. Once done, the thread is cut and falls away, the cork hops off the plank with a somersault and knocks into the neck: the cork squashes, the bottle jolts, the masts sway, the pennants snap and a glint crosses the glass. Harbor 3.0 installed, with Open and Reinstall. Reinstall pulls the cork, ties the thread on again, rolls up the sails, lays the masts down and starts over. One time in two, in the demo, the install stops halfway: the thread slips, the mizzen that was going up falls back onto the deck with a bounce, the winch spins back and its thread goes slack; in red Installation failed, below Couldn’t write to disk, and the pill becomes Retry, which takes up the thread, raises the mast again and goes on from where it stopped. Cancel, during the install, rolls up the sails and lays the masts back down.

How it works

Everything follows one number

Chunks of files of every size arrive, now and then after a stall, and a critically damped spring carries the shown part towards the arrived part and never goes back. Everything comes from that number: every mast has its stretch (the foremast between 3 and 23%, the mainmast between 19 and 39, the mizzen between 35 and 55), every sail one to unroll and one to fill, the pennants the last. So Cancel and Reinstall have no animation of their own: they take the number back to zero and the ship goes back to rest by itself, sails rolled up and masts down.

The masts are on a sprung hinge

Every mast is an angle with its speed: the thread pulls it towards the angle of its stretch with a lightly damped spring (stiffness 95, damping from SPRING), and when the stretch ends the hinge clicks home with a little kick of speed, so the mast arrives upright and rings a few times. The boom and the gaff are tied to the mast and swing open as it rises: folded along it while it is low, square to it when it is up. When the thread slips the mast has no spring any more: it falls under its own weight (an acceleration in proportion to the sine of its angle) and bounces on the deck losing two thirds of its speed. The winch turns by how high the masts are, so it spins back by itself when one falls.

The sails are cloth on springs

Every sail is a grid of 9 by 7 points in three dimensions, joined by sticks along the sides and across the diagonals, integrated with Verlet in steps of 1/120 of a second and eight rounds of constraints. The points along the mast, along the gaff and along the rolled edge are held, and the rolled edge comes down to the boom as the sail unrolls. The wind pushes the cloth out of its plane, in gusts that run along the sail, and the sticks across the sail are up to 10% longer than the flat sail: so the cloth can only belly out, and the free leech trembles. The jib is the same thing, a triangle that unrolls off its stay towards its clew.

The light tells the shape

The sail is drawn quad by quad, in a slightly oblique projection that shifts the points by their depth. For every point the normal comes from its neighbours left and right, above and below; a quad’s light is the average of its four corners, so the cloth reads as one surface and not a mosaic. The colour runs from the shadow of the cloth to the warm white of the sail, then the seams of the cloths and the bolt rope along the edge are drawn on top.

The glass that bends

Everything inside the bottle is painted on a second canvas and copied into the bottle one row of pixels at a time: the row at distance u from the axis takes the row u + (arcsin(0.93 u) ÷ arcsin 0.93 − u) × 0.85, the law of a glass cylinder. The ship is magnified in the middle and squeezed towards the glass, as in real bottles. On top, the glass in layers: its green thickness (between the outline and the inner one, with evenodd), the thick base, the long highlight of the window, the shoulder, the neck and the lip, and on the plank the shadow with the light the bottle gathers into a streak.

The cork, and for those who cannot see it

At the end the thread is cut, the cork follows a Bézier curve from the plank to the mouth turning a full somersault, goes in with power3.in and squashes with elastic.out; at the same moment the bottle jolts, every mast takes a kick of speed and a glint crosses the glass. The scene is a role=progressbar with aria-valuenow and an aria-valuetext that names the phase, the component has aria-busy while it works and an aria-live line says when it starts, stops and ends. A click from a script, the library preview’s, never cancels or opens: after the result it installs again.

Values to change

NameFileDefaultWhat it changes
SECONDSscript.js7How long the demo’s install takes, in seconds (a real one follows its files).
SIZEscript.js84.2The megabytes of the app to install.
FAIL_EVERYscript.js2In the demo, one install in how many goes wrong (0 never).
FAIL_ATscript.js0.5How far the install that goes wrong gets, from 0 to 1: the mast that was going up falls back.
SPRINGscript.js1How much a mast rings on its hinge when it gets upright: 0 not at all, 1 a lot.
WINDscript.js1How much wind fills the sails: 0 none, 1 a steady breeze.
WAVEscript.js1The wave of the resin sea: 0 flat, 1 choppy.
REFRACTIONscript.js1How much the curved glass magnifies the ship in the middle and squeezes it towards the edges: 0 not at all.
LABELSscript.js{ busy: 'Cancel', retry: 'Retry', done: 'Open', opening: 'Opening…' }The button’s words while installing, after an error and at the end (at rest it is the one in index.html).
WORDSscript.js{ copy: 'Copying files', config: 'Configuring', finish: 'Finishing up', done: 'installed', fail: 'Installation failed', why: 'Couldn’t write to disk', canceled: 'Installation canceled' }The words under the bottle: the three phases, the end, the error and its reason, the cancel (at rest they are the ones in index.html).

Try them in the Playground

When to use it

For a wait of a few seconds that ends with something ready: installing an app or an extension, an update, setting up a project, an export «bottled» into a package. The ship rigged piece by piece shows how far along things are without reading the percentage, and the cork going in is a «done» you can feel. For a one second load or a list of files it is too much: a plain bar is better.

On phones

The drawing is 380 units wide and 200 tall and fits the width: on a 390 pixel screen the component is 358 wide and about 240 tall, with nothing spilling over. The canvas paints at the screen’s real density, up to twice, and the row by row refraction follows the real pixels. The pills are 38 tall; under a finger they squash and spring back, and touching Install takes up the thread as the pointer does. The animations are the same as on a computer.

With reduced motion

No springs, gusts, waves or somersaults: every mast goes from laid down to upright with a fade when it gets halfway along its stretch, every sail appears already filled with a still belly, the pennants appear without streaming, the sea keeps still and the cork vanishes from the plank and reappears in the neck. If the install stops, the mast goes back down with a fade. The words change with a fade, and the ship is still rigged one piece at a time, because it is what says how far along things are.

Pitfalls

  • The green thickness of the glass covered the whole bottle: the two outlines, the outer and the inner, each began with beginPath, so the second wiped out the first and the evenodd fill filled the whole inside. The inner outline is now added to the same path.
  • At the knock of the cork the sails vanished for a moment: the jolt bends the masts back a few degrees, under the «mast upright» threshold that switched the cloth on, and the cloth went off and started again flat. The threshold is now well below the sway.
  • The sails looked like crumpled paper: every stick was longer than the flat sail, the upright ones too, and gravity and a forward push pulled the cloth within its plane, so it puckered instead of filling. Now only the sticks across the sail are longer (and the diagonals a little), the push is only out of the plane and the light is the average of the four corners of each quad.
  • The thread between the bowsprit and the neck was black: it was drawn after the ship’s restore, which put back the colour from before. The colour is now set again after it.
  • The folded gaff pointed down through the hull while the mast was laid flat (the «aft» direction of a mast lying down is downwards): the boom and the gaff now swing open as the mast rises and lie along it while it is down.
  • The cork only went in as far as the lip and seemed to sit outside: it now goes in by two thirds, and over the part inside the neck the green of the glass and its highlight are drawn again, so it is seen through the glass.
  • The word «installed» only came at the end of the glint, two seconds after the knock: it now changes the instant the cork strikes.

Libraries

GSAP 3.15.0, Archivo (font variabile) 5.3.0.

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

Testing

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