Skip to content
animorio
IT
Back to the library

The blinking window

A drawn scene and, in front of it, a window showing one of its details up close. Every now and then the window blinks: it closes into an almond and then a line, the scene changes, and it reopens on another detail.

Images, Transitions, SVGGSAP, CSS onlyMediumFree

The code

A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN.

<!doctype html>
<html lang="it">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>La finestra che sbatte le palpebre</title>
  <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="scena" data-scena>
    <!-- lo sfondo: una scena disegnata per strato, una sola visibile alla volta -->
    <svg class="sfondo" data-sfondo viewBox="0 0 1600 1000" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false"></svg>

    <p class="nota" data-nota>Ogni tanto la finestra sbatte le palpebre e si riapre su un’altra scena. Premila per farlo subito.</p>

    <!-- la finestra: la stessa scena vista da vicino, dentro un’apertura a mandorla -->
    <button class="finestra" type="button" data-finestra data-autoclic aria-label="Cambia scena">
      <span class="finestra__occhio" data-occhio>
        <svg class="finestra__vista" data-vista viewBox="0 0 160 90" preserveAspectRatio="xMidYMid slice" aria-hidden="true" focusable="false"><use data-uso href="#" /></svg>
      </span>
      <span class="finestra__fessura" aria-hidden="true"></span>
      <span class="finestra__angoli" aria-hidden="true"></span>
      <span class="finestra__didascalia">
        <span data-dettaglio></span>
        <span class="finestra__luogo" data-luogo></span>
      </span>
    </button>
  </main>

  <!-- le scene, disegnate una volta sola: le usano sia lo sfondo sia la finestra -->
  <svg class="nascosto" aria-hidden="true" focusable="false"><defs data-scene></defs></svg>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <script src="script.js"></script>
</body>
</html>

What it does

A drawn scene fills the screen: the sea at sunset, a wood, a city at night, a field. In front of it, a 16:9 window with four viewfinder corners shows one of its details enlarged: the boat, the cabin with its smoke, the cat on the sill, the kite. After a couple of seconds the window blinks: the opening narrows from top and bottom like an eye, becomes an almond and then a line of light; while it is closed the scene behind fades into the next one, and the window reopens on the new detail. Meanwhile the detail slowly drifts and the scene behind moves closer. Pressing the window makes the blink come at once.

How it works

One scene, twice

Each scene is an SVG group drawn once inside a defs. The background uses it with a full-screen use (preserveAspectRatio xMidYMid slice), the window with another use inside an SVG whose viewBox is tight on the detail: being vector, enlarged four times it stays sharp. The gradients live inside the group and follow both.

The eyelid

The opening is a clip-path: ellipse(80% calc(var(--palpebra) × 82%) at 50% 50%). With the variable at 1 the ellipse covers the corners too; going down to 0 it narrows only in height, and the rectangle becomes first an almond and then a line. GSAP animates the CSS variable, and the same variable lights the line of light and slightly squashes the content.

The blink

A repeating timeline: 2.4 seconds open, 0.9 to close with power3.inOut, a callback that changes the scene, 0.25 in the dark, 1.1 to reopen. The scene behind fades over 0.7 seconds exactly while the window is closed, so the change is never seen with the eye open.

Alive even when still

While the window is open, the viewBox drifts by 6% of its width, like a moving shot, and the scene behind goes from scale 1 to 1.05. Pressing the window makes the timeline jump to the blink label, but only if the eyelid is open: if it is already blinking, it finishes.

Values to change

NameFileDefaultWhat it changes
SCENEscript.js4 sceneFor each scene: the place, the detail’s name, the detail’s 16:9 box and the function that draws it (SVG in 1600 × 1000).
APERTA, CHIUDE, BUIO, APREscript.js2.4, 0.9, 0.25, 1.1The seconds of the blink: eye open, closing, in the dark, reopening.
ellipse(80% … 82%)style.css80%, 82%The eyelid’s shape: narrower in width, rounder the almond.

When to use it

To tell a place or a product through details: the overall view behind, the thing to look at in front, and a gesture that suggests a gaze.

On phones

The window takes almost the full width; the background, cropped to the centre, still shows the four details, which all sit in the middle part of the scene.

With reduced motion

No blink, no drift and no zoom: the window stays open and, when pressed, scene and detail move to the next with a quarter-second fade.

Pitfalls

  • If the fade’s opacity sits on the scene group, the window that uses it inherits it too: it belongs on a group around the background’s use.
  • An ellipse that must cover the whole rectangle when open needs radii larger than half: with 80% and 82% the corners are inside.
  • The change must be a callback inside the timeline, not a separate timer: that way it stays in sync with the dark even when pressing the window jumps ahead.
  • Keep the details in the middle of the scene: with slice, the sides are lost on phones.

Libraries

GSAP 3.15.0, Mona Sans (font variabile) 5.3.0.

Weight: 19 KB of our own files.

Testing

Tested on 27 September 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: Done

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