Skip to content
animorio
IT
Back to the library

The cuckoo clock

SVGGSAPAdvancedCompressed weight: Demo 13 KBGSAP 28 KBFreeDownload the zip

A carved wooden cuckoo clock, drawn entirely in SVG: the hands race to the hour, the little doors spring open with a bounce, the bird bows once for every hour, the figures dance on the balcony and the pendulum speeds up; then the doors slam and the clock shakes on its nail. A click moves on to the next hour.

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>L’orologio a cucù</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/fraunces@5.3.0/full.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/standard.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="pagina">
    <header class="testo">
      <p class="etichetta"><span>Kuckuckhaus, clockmakers</span></p>
      <h1 class="titolo">
        <span class="riga"><span>Carved by hand.</span></span>
        <span class="riga"><span>Opening on the hour.</span></span>
      </h1>
      <p class="corpo"><span>Our first clocks are still on the bench, one leaf at a time. The shop opens this winter, and the bird will let you know.</span></p>
      <p class="ora" aria-live="polite"><span class="ora__dentro">It’s <span class="ora__maschera"><span class="ora__parola" data-parola>three</span></span> o’clock</span></p>
      <p class="aiuto"><span>Click the clock to strike the next hour.</span></p>
    </header>

    <div class="palco">
      <button class="orologio" type="button" data-orologio aria-label="Strike the next hour">
        <svg class="orologio__svg" data-svg viewBox="-12 -40 444 872" aria-hidden="true" focusable="false">
          <defs>
            <linearGradient id="g-muro" x1="0" y1="0" x2="1" y2="1">
              <stop offset="0" style="stop-color: var(--legno-chiaro)" />
              <stop offset="0.55" style="stop-color: var(--legno)" />
              <stop offset="1" style="stop-color: var(--legno-scuro)" />
            </linearGradient>
            <linearGradient id="g-pilastro" x1="0" y1="0" x2="1" y2="0">
              <stop offset="0" style="stop-color: var(--legno-scuro)" />
              <stop offset="0.45" style="stop-color: var(--legno-luce)" />
              <stop offset="1" style="stop-color: var(--legno)" />
            </linearGradient>
            <radialGradient id="g-quadrante" cx="0.42" cy="0.38" r="0.7">
              <stop offset="0" stop-color="#fbf4e2" />
              <stop offset="0.7" stop-color="#f1e3c2" />
              <stop offset="1" stop-color="#dcc597" />
            </radialGradient>
            <linearGradient id="g-buio" x1="0" y1="0" x2="0" y2="1">
              <stop offset="0" stop-color="#140c07" />
              <stop offset="1" stop-color="#2c1a0f" />
            </linearGradient>
            <linearGradient id="g-notte" x1="0" y1="0" x2="0" y2="1">
              <stop offset="0" stop-color="#1d2a40" />
              <stop offset="1" stop-color="#3d5068" />
            </linearGradient>
            <linearGradient id="g-pigna" x1="0" y1="0" x2="1" y2="0">
              <stop offset="0" stop-color="#2a1b11" />
              <stop offset="0.4" stop-color="#6e4c30" />
              <stop offset="1" stop-color="#22150d" />
            </linearGradient>
            <linearGradient id="g-ottone" x1="0" y1="0" x2="1" y2="1">
              <stop offset="0" stop-color="#f0d58a" />
              <stop offset="0.5" stop-color="#c39a45" />
              <stop offset="1" stop-color="#7d5d22" />
            </linearGradient>
            <filter id="f-ombra" x="-20%" y="-20%" width="140%" height="140%">
              <feGaussianBlur stdDeviation="9" />
            </filter>
            <clipPath id="c-muro"><path d="M210 70 L60 196 L70 196 V560 H350 V196 L360 196 Z" /></clipPath>
            <clipPath id="c-sinistra"><rect x="-40" y="-60" width="250" height="400" /></clipPath>
            <clipPath id="c-destra"><rect x="210" y="-60" width="250" height="400" /></clipPath>
            <!-- la pigna, disegnata dallo script: punta di sopra in (0 0) -->
            <g id="pigna" data-pigna></g>
            <!-- le maglie delle catene, una per catena: lo script le fa scorrere -->
            <g data-maglie></g>
          </defs>

          <!-- tutto l'orologio, appeso al chiodo: trema quando le porte sbattono -->
          <g data-tutto>
            <path class="ombra" d="M210 18 L-2 214 L56 214 V600 H364 V214 L422 214 Z" transform="translate(10 16)" filter="url(#f-ombra)" />

            <!-- il pendolo, dietro la cassa: gira intorno al perno -->
            <g data-pendolo>
              <rect x="207" y="462" width="6" height="292" rx="2" class="lb" />
              <rect x="208.2" y="462" width="1.6" height="292" class="ll" opacity="0.5" />
              <g data-lente></g>
            </g>

            <!-- le catene e le pigne -->
            <g data-catene></g>

            <!-- la cassa -->
            <path d="M210 70 L60 196 L70 196 V560 H350 V196 L360 196 Z" fill="url(#g-muro)" />
            <g clip-path="url(#c-muro)" data-venature></g>
            <rect x="70" y="196" width="16" height="364" fill="url(#g-pilastro)" />
            <rect x="334" y="196" width="16" height="364" fill="url(#g-pilastro)" />
            <path d="M78 206 V550 M342 206 V550" class="solco" />
            <rect x="64" y="410" width="292" height="10" rx="2" class="ls" />
            <rect x="64" y="410" width="292" height="3" class="ll" opacity="0.6" />

            <!-- la porticina dell'uccellino -->
            <path d="M174 190 V146 A36 36 0 0 1 246 146 V190 Z" class="lb" />
            <path d="M177 188 V146 A33 33 0 0 1 243 146 V188 Z" class="lc" />
            <path d="M182 184 V146 A28 28 0 0 1 238 146 V184 Z" fill="url(#g-buio)" />
            <rect x="168" y="184" width="84" height="8" rx="2" class="ls" />
            <rect x="168" y="184" width="84" height="2.5" class="ll" opacity="0.7" />
            <g data-porta="sinistra">
              <g data-faccia="davanti">
                <path d="M182 184 V146 A28 28 0 0 1 210 118 V184 Z" class="l" />
                <path d="M186 180 V148 A23 23 0 0 1 206 124 V180 Z" class="ls" opacity="0.55" />
                <path d="M188 178 V149 A20 20 0 0 1 204 127 V178 Z" class="lc" opacity="0.65" />
                <circle cx="205" cy="156" r="2.2" fill="url(#g-ottone)" />
              </g>
              <g data-faccia="dietro">
                <path d="M182 184 V146 A28 28 0 0 1 210 118 V184 Z" class="ls" />
                <path d="M187 180 V149 A22 22 0 0 1 205 126 V180 Z" class="l" opacity="0.6" />
                <path d="M186 182 V148 A23 23 0 0 1 206 124" class="solco" />
              </g>
            </g>
            <g data-porta="destra">
              <g data-faccia="davanti">
                <path d="M238 184 V146 A28 28 0 0 0 210 118 V184 Z" class="l" />
                <path d="M234 180 V148 A23 23 0 0 0 214 124 V180 Z" class="ls" opacity="0.55" />
                <path d="M232 178 V149 A20 20 0 0 0 216 127 V178 Z" class="lc" opacity="0.65" />
                <circle cx="215" cy="156" r="2.2" fill="url(#g-ottone)" />
              </g>
              <g data-faccia="dietro">
                <path d="M238 184 V146 A28 28 0 0 0 210 118 V184 Z" class="ls" />
                <path d="M233 180 V149 A22 22 0 0 0 215 126 V180 Z" class="l" opacity="0.6" />
                <path d="M234 182 V148 A23 23 0 0 0 214 124" class="solco" />
              </g>
            </g>

            <!-- l'uccellino: esce dalla porta, i piedi sul trespolo in (0 0) -->
            <g transform="translate(210 186)">
              <g data-uccello>
                <rect x="-16" y="-1" width="40" height="5" rx="2" class="ls" />
                <g data-corpo>
                  <path d="M-4 -12 L-6 0 M4 -12 L3 0" class="zampe" />
                  <g data-coda><path d="M10 -18 L44 -34 L47 -27 L42 -24 L45 -19 L14 -10 Z" class="u-scuro" /></g>
                  <path d="M-22 -26 C-22 -40 -2 -44 12 -34 C22 -26 18 -10 2 -9 C-12 -8 -22 -14 -22 -26 Z" class="u-corpo" />
                  <path d="M-21 -24 C-18 -15 -8 -11 2 -11 C-6 -16 -12 -22 -14 -30 Z" class="u-petto" />
                  <g data-becco-su><path d="M-31 -38 L-44 -35 L-30 -33 Z" class="u-becco" /></g>
                  <g data-becco-giu><path d="M-31 -33 L-41 -32 L-30 -30.5 Z" class="u-becco" /></g>
                  <circle cx="-22" cy="-37" r="10" class="u-corpo" />
                  <circle cx="-24" cy="-39" r="1.9" class="u-occhio" />
                  <g data-ala><path d="M-10 -30 C2 -38 20 -32 28 -22 C14 -20 0 -20 -10 -30 Z" class="u-scuro" /><path d="M-4 -29 C6 -31 14 -28 20 -24" class="u-penna" /><path d="M-2 -26 C8 -27 14 -25 18 -22" class="u-penna" /></g>
                </g>
              </g>
            </g>

            <!-- il quadrante -->
            <circle cx="210" cy="300" r="104" class="lb" />
            <circle cx="210" cy="300" r="101" class="ls" />
            <g data-perline></g>
            <circle cx="210" cy="300" r="91" class="lb" />
            <circle cx="210" cy="300" r="89" fill="url(#g-quadrante)" />
            <g data-tacche></g>
            <g data-numeri class="numeri"></g>
            <g data-ore>
              <path d="M207 306 L207.5 270 C200 268 200 258 208 256 L210 247 L212 256 C220 258 220 268 212.5 270 L213 306 Z" class="lancetta" />
              <circle cx="210" cy="263" r="3.2" fill="#efe1bf" />
            </g>
            <g data-minuti>
              <path d="M208.4 308 L208.6 242 C203 240 203 233 208.6 232 L210 218 L211.4 232 C217 233 217 240 211.4 242 L211.6 308 Z" class="lancetta" />
              <circle cx="210" cy="237" r="2" fill="#efe1bf" />
            </g>
            <circle cx="210" cy="300" r="6.5" fill="url(#g-ottone)" />
            <circle cx="210" cy="300" r="2.4" fill="#3a2614" />

            <!-- il balconcino coi ballerini -->
            <path d="M118 526 V452 A92 36 0 0 1 302 452 V526 Z" class="lb" />
            <path d="M123 526 V454 A87 32 0 0 1 297 454 V526 Z" fill="url(#g-notte)" />
            <g data-sfondo></g>
            <ellipse cx="210" cy="510" rx="80" ry="11" class="lb" />
            <ellipse cx="210" cy="508" rx="78" ry="9" class="ls" />
            <g data-ballerini></g>
            <g data-ringhiera></g>

            <!-- il grembiule sotto la cassa -->
            <g data-grembiule></g>

            <!-- il tetto -->
            <g data-tetto></g>
            <!-- le foglie intagliate -->
            <g data-foglie></g>
          </g>
        </svg>
      </button>
    </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.

25 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 cuckoo clock” from Animorio and fit it into my site

What it does

On the striped wallpaper of a made-up workshop, Kuckuckhaus, sits the page of a shop that opens soon: the title on the left, on the right a cuckoo clock hanging on a nail. It has a pitched roof covered in round shingles, a crest of carved oak leaves on the ridge, a bargeboard with scallops and holes, leaves along its sides and around the dial, an ivory dial with numbers and beads, a little balcony with a turned railing and, below, the pendulum with its leaf bob and two pine cones hanging from the chains. As the page opens the clock drops onto its nail and swings until it settles. The hands race to the full hour, pass it slightly and settle back; the line “It’s three o’clock” changes its word with a jump. The little doors spring open with a bounce and the clock jolts; out comes the bird, growing as it comes forward, and bows as many times as the hour: beak open, wings and tail up, and back up with a bounce. With every bow the striking weight drops by a notch. Meanwhile on the balcony four figures, two girls with pompom hats and two boys with felt hats, go round in a circle hopping and twirling, bigger at the front and smaller at the back, and the pendulum beats faster and wider. Then the bird goes back in, the doors slam shut and the whole clock shakes on its nail, with the cones swinging. The minute hand slowly starts again, the time weight drops with the minutes, and after a moment the next hour comes; when a cone is almost at the bottom, its chain pulls it back up with a bounce. A click brings the next hour at once; hovering with the mouse makes the little doors open just a crack.

How it works

A clock carved by the script

The page holds the skeleton of the clock (case, doors, bird, dial, balcony); everything that repeats is carved by the script with a seeded random number generator, so it comes out the same on every visit. An oak leaf is a computed path: its width follows a sine and the lobes are a second sine wave along the edge; each leaf has an offset shadow, the wood, a highlight on its upper side and veins. The shingles sit on two roof slopes: a group with a matrix that lays the x axis along the slope, rows of round shingles from the inside out, and a clipPath that cuts each slope in half, so they meet cleanly at the ridge.

The colour of the wood

WOOD is a single colour: the script derives five shades from it, mixing it towards black and towards a warm white, and writes them as CSS variables on the SVG. The wooden parts only have a class (.l, .ls, .lb, .lc, .ll) and the gradients use the variables too, with style="stop-color: var(--legno)", so changing one value changes the whole clock, shadows and highlights included.

One state, drawn every frame

Doors, bird, bow, beak, wings, dance, pendulum, cones and shake are numbers in one object; GSAP animates those numbers and a function on the ticker rewrites the transform attributes. So the doors fold on their hinge with scale(x 1) and, past zero, show their back; the figures go round an ellipse and are reordered front and back only when the order changes; the bird bows around its feet.

A real pendulum

The pendulum is 29 centimetres long: its angular frequency is the square root of g over L, corrected for wide swings by dividing by 1 + θ²/16, and time comes from a phase that the ticker moves forward. During the show a multiplier on the phase rises to 1.9 and the swing grows by a quarter, then they slowly come back. The cones follow the clock: the time weight drops with the minutes, the striking weight by a notch with every bow, the free end of the chain rises by the same amount and the links, a pattern with a patternTransform, slide along with them.

Direction

Every hour is a timeline: the hands race with back.inOut (they pass the hour and come back), the doors open with back.out and a small jolt, the bird comes out, the bows are four tweens each (down with power2.in, up with back.out), the figures and the pendulum speed up; at the end the doors shut with power4.in and the shake is a series of alternating rotations that die away, around the nail. When it ends a delayedCall waits for the next hour; a click cancels it and calls the next hour at once, putting everything back in place first.

Values to change

NameFileDefaultWhat it changes
HOURscript.js3The hour of the first show, from 1 to 12: the hands start a little after the hour before and race to it.
BOWSscript.js0How many times the bird bows: 0 = as many as the hour, like a real clock; any other number fixes them for every hour.
PENDULUMscript.js13How far the pendulum swings, in degrees each way (0 = still). Wider means slightly slower, like a real pendulum.
WOODscript.js'#7a4a2b'The colour of the wood: the shadows and highlights of the carving are made from it.
SPEEDscript.js1The speed of everything, pendulum included: 2 is twice as fast, 0.5 half as fast.
RESTscript.js2.5Seconds of rest after a show before the next hour; with 0 the next hour only comes with a click.

Try them in the Playground

When to use it

For a coming-soon page or the first screen of a craft workshop, a clock or wooden toy shop, a mountain bakery, a chalet hotel; for a countdown, a friendly 404 or a page that marks a time.

On phones

The same animation with the sizes adapted: the title moves to the top, centred, and the clock fills the space below; on a short screen the long text is hidden. A tap brings the next hour.

With reduced motion

Nothing moves: pendulum and cones stay still and the hands are already on the hour; the little doors fade away and the bird appears in their place, then fades as the doors come back. The hour line changes with a fade.

Pitfalls

  • The two roof slopes, left whole, overlapped at the ridge and one covered the other at an angle: each has a clipPath cutting it on the ridge's vertical, so they meet on a straight line.
  • One slope's matrix mirrors the axes (determinant -1): the shingle arcs must be thought of in the slope's own space, not the page's, or they turn the wrong way.
  • Doors folded with scale(x 1) become a line at zero, and their back would look like their front: below a minimum the scale stays at 0.02 and, past zero, the back shows with a different drawing.
  • The figures going round must pass behind one another: they are reordered in the DOM by depth, but only when the order really changes, not on every frame.
  • A click during a show left the bird out or the doors half open: every hour starts by bringing everything back shut in three tenths of a second, then goes on.
  • The pendulum cannot be a back-and-forth tween: when it speeds up it would jump position. It is a phase that moves forward, and what speeds up is the speed of that phase.

Libraries

GSAP 3.15.0, Fraunces (font variabile) 5.3.0, Mona Sans (font variabile) 5.3.0.

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

Testing

Tested on 10 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 animations