
The send button
Buttons, Transitions, SVG
NewFree
A book reader's Sync library button is a piece of electronic paper in its bezel: pressed, it does a real e-reader refresh, a black flash, a negative, white and the new picture laid down in bands of rows. While it syncs the circular arrows turn in jerks, a few frames a second in pure black and white, each leaving a ghost of the one before; when it is done only the words refresh, with their own little flash.
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 e-ink button</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/literata@5.3.0/opsz.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- the e-ink button: a book reader's Sync library button drawn as a piece of electronic paper in its bezel. The
screen is a low resolution canvas with real e-ink refreshes: flashes, a spinner in black and white at a few
frames a second leaving ghosts behind it, and a partial refresh of the words when it is done -->
<div class="eink" data-eink>
<button class="eink__btn" type="button" aria-label="Sync library. Last synced 2 days ago" data-btn data-autoclic>
<span class="eink__bezel" aria-hidden="true"></span>
<span class="eink__screen" aria-hidden="true">
<canvas class="eink__canvas" width="312" height="92" data-canvas></canvas>
<span class="eink__glass"></span>
</span>
</button>
<p class="sr" 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>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.
20 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 e-ink button” from Animorio and fit it into my siteThe screen is a canvas of 312 by 92 pixels, one per CSS pixel, scaled up with image-rendering: pixelated: on double density screens each pixel of ink is a little square of four, and the pixels show up close as on a real reader. Each screen is drawn in a hidden canvas: the arrows are SVG paths passed to Path2D and turned, the words are real type in Literata, rasterised with fillText.
The grey of every pixel goes through an ordered dither with the 4 by 4 Bayer matrix: for normal updates it comes down to 16 levels (the matrix chooses between the two nearest), for fast ones to pure black and white, like a reader's fast mode. So the rule, the grey line underneath and the edges of the letters show the regular pattern of the dither.
What shows is not the picture but a buffer of numbers from 0 (black) to 1 (white): the state of the ink. A full refresh takes it exactly to the new picture; an update without a flash moves it towards the new picture but leaves a share of the old one (GHOST): that is the ghosting, and it builds up frame after frame. The colour of each pixel is a mix of the paper and ink colours from the CSS, with a little fixed grain.
Every change of the screen is a step in a queue: GSAP's ticker (requestAnimationFrame) adds up the time, with the step capped at 1/30 of a second, and when the step's time has passed it changes the ink once. The full refresh is black, negative, white and seven bands of rows; the spinner is one frame every 1/SPIN_FPS of a second, turned by SPIN_STEP degrees, updated only in the icon's rectangle. The canvas is drawn again only when the ink has changed.
At the end of the sync only the rectangle of the words flashes black and white and shows the new words clean; the tick in the arrows comes in with the same step, over the ghosts of the turning, which stay. The number of spinner frames is a whole number of half turns, so the arrows stop exactly where they started.
| Name | File | Default | What it changes |
|---|---|---|---|
SYNC | script.js | 2.6 | The seconds the pretend sync lasts. |
SPIN_FPS | script.js | 5 | The frames a second of the spinner: e-ink is slow. |
SPIN_STEP | script.js | 30 | How many degrees the arrows turn at each frame. |
GHOST | script.js | 0.26 | How much of the old picture a fast black and white update leaves behind. |
FLASH | script.js | 0.11 | The seconds of each step of the full refresh (black, negative, white). |
HOLD | script.js | 4 | The seconds Synced stays before the button is back at rest. |
NEW_BOOKS | script.js | 3 | How many new books the sync finds. |
TEXTS | script.js | { rest: ['Sync library', 'Last synced 2 days ago'], … } | The words on the screen at each moment: the title and the line underneath. |
For a reading app, a notes archive, a publishing site or any product that wants the calm character of paper: syncing, downloading, refreshing. Electronic ink is slow on purpose: it belongs on an action that takes a few seconds, not on a button pressed all the time.
The bezel is 340 pixels wide and fits a 390 pixel screen; under 380 pixels it shrinks a touch. On double density screens the pixels of the ink are little squares of four, as on a reader. A touch takes the bezel down like a click.
No flashes and no spinning arrows: the words change in place with an update without a flash, Syncing library, Downloading new books, Synced · 3 new books with the tick, and then back to rest; the bezel does not rise under the mouse.
GSAP 3.15.0, Literata (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 6.9 KB; the libraries: GSAP 28 KB.
Tested on 5 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.