
The slide-to-confirm button
Buttons, Physics, Transitions
NewFree
A pomodoro technique timer where the time is a real tomato ripening on the vine: it starts green and hard and over the twenty-five minutes turns yellow, orange and glossy red, from the bottom up to the stalk as on the plant, with the skin tightening, the reflections getting crisp and the sepals opening, while the branch bends under the weight. At zero the tomato lets go, drops with a soft bounce and the break begins, with a yellow flower opening in its place.
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 ripening tomato timer</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/full.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="stage">
<!-- a pomodoro timer where the time is a real tomato ripening on the vine: it starts green and hard, and over the
twenty-five minutes it turns from the blossom end up, through breaker, turning, pink and light red, to a glossy
red, its skin tightening and its sepals opening, while the branch bends under it. At zero the ripe tomato lets
go at its knuckle, drops with a soft bounce, and the break begins while a new flower opens on the stalk -->
<section class="pomo" aria-label="Pomodoro timer" data-timer>
<div class="pomo__vine" data-vine>
<canvas class="pomo__canvas" aria-hidden="true" data-canvas></canvas>
</div>
<div class="pomo__readout" data-readout>
<span class="pomo__time" role="timer" aria-live="off" aria-atomic="true">
<span class="pomo__digits" aria-hidden="true" data-time></span>
<span class="sr" data-time-words></span>
</span>
<p class="pomo__state"><span class="pomo__word" data-word></span><span class="pomo__detail" data-detail></span></p>
</div>
<div class="pomo__controls" data-controls>
<button class="pomo__reset" type="button" aria-label="Reset" aria-disabled="true" data-reset>
<span class="pomo__turn">
<svg viewBox="0 0 24 24" aria-hidden="true" data-reset-icon><path d="M5.6 9.2A7.2 7.2 0 1 1 4.9 14.2"/><path d="M5.2 4.6v4.8H10"/></svg>
</span>
</button>
<button class="pomo__go" type="button" data-go data-autoclic>
<span class="pomo__shine" aria-hidden="true"></span>
<span class="pomo__go-words" data-go-words>Start</span>
</button>
<span class="pomo__picked" role="img" aria-label="No tomatoes picked today" data-picked>
<svg class="pomo__mini" viewBox="0 0 32 32" aria-hidden="true" data-mini>
<defs>
<radialGradient id="pomo-mini-skin" cx="38%" cy="36%" r="70%">
<stop offset="0" stop-color="#ff7a55"/>
<stop offset="0.45" stop-color="#e2321d"/>
<stop offset="1" stop-color="#a3140f"/>
</radialGradient>
</defs>
<path d="M16 9.2c-3.1-1.7-6.4-1.4-8.6.7-2.7 2.4-3.4 6.6-1.9 10.2 1.6 3.8 5.6 6.4 10.5 6.4s8.9-2.6 10.5-6.4c1.5-3.6.8-7.8-1.9-10.2-2.2-2.1-5.5-2.4-8.6-.7z" fill="url(#pomo-mini-skin)"/>
<path d="M10.4 12.6c.8-1.3 2.2-2 3.4-2.1" fill="none" stroke="#ffd9c8" stroke-width="1.3" stroke-linecap="round" opacity="0.85"/>
<path d="M16 10.4l-1.6-3 1.6 1.1 1.5-2.4.1 2.7 3.1-.6-2.3 2.1 2.9 1.3-3.4.1-.7 2-1.2-1.9-2.6 1.4 1.3-2.2-3.2-.6 3-.9z" fill="#3f7a25"/>
<path d="M16 8.4c.1-1.6.7-2.9 1.8-3.8" fill="none" stroke="#3f7a25" stroke-width="1.6" stroke-linecap="round"/>
</svg>
<span class="pomo__count" aria-hidden="true" data-count></span>
</span>
</div>
<p class="sr" 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.
28 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 ripening tomato timer” from Animorio and fit it into my siteThe tomato is a squat ellipsoid seen a little from above, painted on a hidden canvas. For every pixel, once for each size, the script works out the normal, how high it sits above the bottom of the fruit, the angle round the stalk and a soft noise. The ribs are six grooves starting at the stalk, strong on the shoulders and gone by the middle: they barely change the outline, they bend the normal along the angle. Round the stalk the normals lean inwards: the dimple. So painting means, for every pixel, a few multiplications and two powers, at most thirty times a second.
The local ripeness is the timer's ripeness minus the height on the fruit, plus the noise: the colour rises from the bottom in patches and the shoulders stay green the longest, as on the plant. The colour comes from a table of 256 steps between the stages growers use (green, pale green, breaker, turning, pink, light red, red), mixed as the eye sees them and not in linear light, which went through brown between green and orange. On the green there are small pale round flecks, scattered on a grid round the fruit, that go with the red.
As it ripens the ribs soften and the light narrows: the exponent of the highlight goes from 16 to 136. The window is the reflected view direction compared with the light direction, measured on two axes across it: two panes with the cross between them, soft on the waxy green and crisp on the glossy red. The shade is not grey: the flesh lets light through, so in the shade the colour gets deeper, the colour squared. Underneath there is the light bouncing off the floor, on the rim the light of the sky.
The sepals are six blades in 3D round the axis of the stalk: each starts from a ring at a slant and curls along its length, and is drawn as a tapering leaf, from the farthest to the nearest. Closed they hug the green fruit, open they fold back. The break's flower uses the same blades for its petals, which point down as a bud and then turn back round the cone of anthers. The branch is a cantilever held off the drawing: its shape is the deflection under a weight at the node, and the deflection is a damped spring that follows the weight of the tomato. The leaves, made of toothed leaflets drawn once as Path2D, sit on springs the branch and the pointer stir.
The stalk is a pendulum with the stiffness of the stem: the breeze pushes it while the time runs, the pointer gives it a push. Since turning the image would also turn the highlight, the skin is repainted with the light turned back by the same angle. At zero the tomato lets go at the knuckle with the speed of its swing, falls with gravity, squashes on impact along the vertical round the point of contact and bounces; then it rolls without sliding, and gravity brings it back onto its wide side, because a squat fruit standing up sits higher.
The time left comes from the clock (performance.now), so a tab left in the background ends on time. With QUICK a round started by hand lasts a few seconds and the figures show the real minutes; the figures roll only if the change before was more than a quarter of a second ago. The figures are set in Fraunces: as it ripens the SOFT axis goes from 0 to 100 and the weight from 360 to 560, and in the break they firm up again. The preview's click (detail 0) only starts a whole quick round, work, drop, break and new tomato, and script clicks arriving while it runs do nothing. role="timer" with aria-live off, and an aria-live region that speaks only when the state changes.
| Name | File | Default | What it changes |
|---|---|---|---|
FOCUS | script.js | 25 | The minutes of a focus round. |
BREAK | script.js | 5 | The minutes of the break after it. |
QUICK | script.js | 20 | To try it out, a round started by hand lasts this many seconds (the figures still show the real minutes); 0 = in real time. |
PREVIEW | script.js | [8, 3] | In the library's preview, the seconds of the focus round and of the break. |
BEND | script.js | 1 | How much the branch bends under the tomato: 0 = stiff. |
SWAY | script.js | 1 | How much the tomato swings in the breeze and under the pointer: 0 = still. |
BOUNCE | script.js | 0.34 | How much the ripe tomato bounces when it lands: 0 = it just drops. |
For the timer of a focus or study app, a task manager, a browser extension or a personal site about productivity: wherever the one working wants to see how long is left out of the corner of an eye, without counting. The same pixel painter suits any fruit that changes colour with time (a peach, an apple, a pepper) with another shape and other colour stages.
The component is 360 pixels wide and at 390 it narrows to 358 without spilling over; the drawing is repainted at the new size. A tap on the tomato swings it the other way and a finger sliding sideways stirs the leaves and the fruit, without blocking vertical scrolling. The buttons are 48 and 56 tall. The skin is painted on about fifty thousand pixels at most thirty times a second, and never while it is still.
The tomato still ripens, because it is the time: it changes colour and gloss without moving. No breeze, no swing, the branch does not bend; at zero the tomato does not fall: it fades from the branch and appears on the ground. The flower fades in already open, the new tomato fades in and the figures change without rolling.
GSAP 3.15.0, Fraunces (font variabile) 5.3.0.
Compressed with gzip, the three demo files weigh 21 KB; the libraries: GSAP 28 KB.
Tested on 17 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.