Skip to content
animorio
IT
Back to the library

The halftone that melts away

Images, HoverCanvasMediumCompressed weight: Demo 6.2 KBno librariesFreeDownload the zip

Each render of a CGI studio sits under a flat field of colour, with a black dot wherever the picture is dark: at rest it looks like a halftone print. With the mouse over it the dots shrink one by one, each after its own wait, the colour fades and the full-colour render shows through; move away and the halftone comes back.

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>The halftone that melts away</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/space-grotesk@5.3.0/index.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/jetbrains-mono@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <!-- the work page of a small CGI studio: every render is hidden under a flat field of colour, with black dots where
       the picture is dark; over a card the dots shrink one by one and the field fades, and the render comes through -->
  <main class="pagina">
    <header class="testa">
      <p class="testa__marchio">Lumen Club</p>
      <h1 class="testa__titolo">Selected renders</h1>
      <p class="testa__nota">CGI for brands, 2026</p>
    </header>

    <ul class="griglia">
      <li>
        <a class="carta" href="#" data-carta data-scena="sfera">
          <span class="carta__quadro">
            <canvas class="carta__render" data-render aria-hidden="true"></canvas>
            <canvas class="carta__retino" data-retino aria-hidden="true"></canvas>
          </span>
          <span class="carta__nome">Orbit</span>
          <span class="carta__dove">a perfume launch</span>
        </a>
      </li>
      <li>
        <a class="carta" href="#" data-carta data-scena="anello">
          <span class="carta__quadro">
            <canvas class="carta__render" data-render aria-hidden="true"></canvas>
            <canvas class="carta__retino" data-retino aria-hidden="true"></canvas>
          </span>
          <span class="carta__nome">Halo</span>
          <span class="carta__dove">a headphone brand</span>
        </a>
      </li>
      <li>
        <a class="carta" href="#" data-carta data-scena="pila">
          <span class="carta__quadro">
            <canvas class="carta__render" data-render aria-hidden="true"></canvas>
            <canvas class="carta__retino" data-retino aria-hidden="true"></canvas>
          </span>
          <span class="carta__nome">Stack</span>
          <span class="carta__dove">a furniture fair</span>
        </a>
      </li>
      <li>
        <a class="carta" href="#" data-carta data-scena="arco">
          <span class="carta__quadro">
            <canvas class="carta__render" data-render aria-hidden="true"></canvas>
            <canvas class="carta__retino" data-retino aria-hidden="true"></canvas>
          </span>
          <span class="carta__nome">Arch</span>
          <span class="carta__dove">a music festival</span>
        </a>
      </li>
    </ul>
  </main>

  <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.

21 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 halftone that melts away” from Animorio and fit it into my site

What it does

The work page of a made-up studio, Lumen Club, that makes renders for brands: four cards on an almost black page, each with the project’s name and who it is for. At rest every card is an ultramarine rectangle full of very fine black dots; where the render is light the blue stays solid, so the shapes can be read: a ball, a ring, three stacked boxes, a doorway full of sunset with someone standing against the light. Over a card the dots shrink until they disappear, not all together but each after its own wait, while the blue turns transparent: in half a second the full-colour render is there, with its lights and shadows. Leaving the card the halftone comes back the same way, and the dots reappear in the same order. On a phone one tap uncovers the card and another covers it again.

How it works

The renders

Each card has a canvas with the render, painted by the script with gradients: a dark room and an object lit from the upper left. What matters is that the shadows are really dark, below DARK: that is where the dots go, and the shape can be read in the halftone too.

Where the dots go

The render is read in small, at most SAMPLE pixels wide. The grid has cells as big as the card’s short side divided by CELL[0], never less than CELL[1] pixels. Each cell looks at the brightness of its centre (0.299 red, 0.587 green, 0.114 blue) and, if it is below DARK, gets a dot.

A wait for every dot

Each dot has a wait between 0 and 1, taken from a hash of its row and column: it looks random, but it is always the same, so resizing the window does not make the pattern flicker and the dots come back in the order they left.

One number draws everything

A number p goes from 0 to 1 in DURATION milliseconds, in a straight line. The field is drawn with an opacity of 1 minus p; each dot has a radius of half a cell times 1 minus (p minus its wait) divided by (1 minus the wait), so it stays full until p passes its wait and then shrinks. All the circles share one path and are filled at once.

Which card uncovers

The card to uncover is chosen by the script by looking at which element is under the mouse on every move, so it also works with the preview’s autopilot; with the keyboard the card with the focus uncovers, with a finger the one that was tapped.

Values to change

NameFileDefaultWhat it changes
CELLscript.js[150, 3]The grid: the card’s short side divided by the first number, never less than the second in pixels (as on the site).
DARKscript.js0.26How dark a spot must be to get its dot, from 0 (black) to 1 (white); 0.26 on the site.
DURATIONscript.js520The milliseconds for the dots to melt and come back (240 on the site).
SAMPLEscript.js1024The largest width the render is read at to place the dots.

Try them in the Playground

When to use it

For a studio’s work page, a product gallery or an archive of covers: at rest the page has a single graphic look, and the real picture is earned by passing over it.

On phones

The same, with the cards one under the other: one tap melts the halftone and another puts it back.

With reduced motion

The dots do not shrink: the whole halftone fades away and comes back the same way.

Pitfalls

  • A halftone does only two things, dot or nothing: if the render has few dark areas you only see a flat silhouette. The faces in shadow have to be painted below DARK, so the shape can be read even when covered.
  • Hover is decided by the script, not by :hover or pointerenter: the preview’s autopilot sends made-up events, and a pointerleave bubbling up from a child would close the card while the mouse is still on it.
  • Thousands of circles drawn one by one are slow: they all share one path and are filled with a single fill.

Libraries

Space Grotesk (font variabile) 5.3.0, JetBrains Mono (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 6.2 KB, and no libraries are needed.

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