051

Interactive Halftone Image

£10 or the whole Vault, £99

An image rendered live as a halftone dot grid the pointer drags through: dots swell, smear along the stroke and split into misregistered ink at speed, then settle back elastically.

advanced
6 more details
canvashalftonepointer-effectsvelocity-reactivemicro-interactionload-sequence

This demo reads better at your own screen size than in the frame below:

About this effect

A photograph rebuilt live as a halftone dot grid on a single canvas, running on GSAP's core ticker with zero plugins. Every dot is sized from the image's sampled luminance, so any same-origin photo drops straight in with no preprocessing.

Read the full effect overview

The pointer drags an ink wake through the print: dots swell with pointer speed, elongate and smear along the stroke, and above a velocity threshold split into two misregistered ink layers, like a print pulled mid-pass. Lift off and the whole wake settles back with an elastic wave.

Interactive Halftone Image - GSAP animation effect preview

What's included

8 items
  • Live luminance sampling: the image is drawn to an offscreen canvas at grid resolution and read once, so any same-origin photo becomes a halftone with no preprocessing
  • Velocity-reactive wake with three layered responses to one input: dot swell, directional smear, and misregistered ink splitting, all scaled by pointer speed
  • Elastic settle driven by per-dot springs in typed arrays, so thousands of dots relax with natural overshoot and no per-dot tweens
  • One-off print pass on load: a sweep resolves the image from scattered noise into the finished halftone in about a second
  • Autonomous idle sweep keeps the print alive after a few seconds without input; any real pointer or touch input takes over instantly
  • Touch-first pointer events, so a finger drag drives exactly the same wake as a mouse
  • Batched rendering: the entire grid draws in at most three canvas fills per frame, with the resolution capped at 2x DPR
  • Configured entirely through data attributes: cell size, ink colour, paper colour and wake strength

Perfect for

5 use cases
  • Portfolio and agency hero portraits with a centrepiece visitors touch rather than scroll past
  • Music, zine and editorial landing pages with a genuine print-shop texture
  • Speaker, artist and founder profile pages where one photograph carries the whole design
  • About and team pages with portraits that answer the visitor's pointer
  • Campaign microsites built around a single striking image

How it works

1 section

The source image is drawn to an offscreen canvas at one pixel per grid cell and sampled once with getImageData; each cell's luminance sets its resting dot radius, so bright areas print as big paper dots on the dark ground. A single gsap.ticker callback runs everything: pointer velocity is stamped into typed-array fields for swell, smear and ink split, per-dot springs relax those fields back toward rest with a slight elastic overshoot, and the grid is redrawn as batched Path2D fills, ink layers under paper. The load-in print pass and the idle sweep are ordinary GSAP tweens driving the same wake function as the real pointer, so every input shares one code path.

Difficulty Advanced
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Lighthouse, as measured

Google Lighthouse on this effect's demo, from the latest scan. A measurement of the demo as shipped, not a promise for your page.

Accessibility
100
Best practices
100

No performance score, on purpose. That figure depends on how you deploy: your server's compression and caching, your CDN, the connection and the device doing the test, none of which the code controls. The same page can score very differently on two consecutive runs, so measure it where it will live.

Paid effect

Purchase to unlock the code.

Buying Interactive Halftone Image opens the HTML, CSS and JavaScript source, the full documentation, an AI setup prompt for your editor, and every update we ship to it. Standard license: unlimited personal and commercial projects. Every paid product is tested for desktop and mobile layouts, reduced-motion handling, animation cleanup and keyboard controls wherever there is something to operate.

£10 Standard license, unlimited projects

Browse free effects

Or the whole library: the Vault, £99, one payment.

Documentation

Quick Start

1. Add to your HTML <head>:

Code snippet omitted: it ships with the download.

2. Add before the closing </body> tag:

Code snippet omitted: it ships with the download.

No GSAP plugins are needed.

3. Add the markup to your <body>:

Code snippet omitted: it ships with the download.

That is the whole contract: a container carrying data-halftone with one <img> inside it. The script builds the <canvas> itself, waits for the image to decode, samples it, and takes over.

Using It With Your Own Design

What the effect needs from your markup. A container with data-halftone holding one <img>. The container must be positioned (position: relative works) and must have a real height, because the script sizes its canvas from the container's box; a height-less div collapses to nothing and so does the print. The image is cover-cropped to the container's aspect ratio, exactly like object-fit: cover.

Any same-origin image works. Swap the portrait by changing the src; the grid resamples automatically, no other change needed. The one hard rule is that the script reads pixels with getImageData, so the image must be same-origin, or served with CORS headers and marked crossorigin="anonymous". A hotlinked image without CORS clearance throws a SecurityError; the script catches it, removes .has-js and leaves your plain <img> on the page rather than a blank canvas. High-contrast photos with a dark background print best, because luminance is the dot size: bright areas become big paper dots and the darkest areas fall back to a fine ground tint (HT_TONE_FLOOR at the top of script.js; drop it to 0 if you want the shadows to print as bare ground).

What is only the demo's styling and can be deleted wholesale: the prompt line (.ht-prompt) and the crosshair cursor. The script reads none of them. The ground colour is not hardcoded in the script: the canvas paints whatever background the container has, so restyle .ht-stage (or your own class) and the print bed follows.

CSS the effect depends on. Three blocks in style.css must survive a reskin:

Code snippet omitted: it ships with the download.

Keep the <img> real and give it a real alt: it is the no-JavaScript fallback, the slow-connection placeholder, and the accessibility text. If GSAP fails to load (blocked CDN), or the image fails, the script removes .has-js and the plain photograph comes back on its own.

The second ink is a constant. data-halftone-ink sets the leading ink of the misregistration split; its warm partner is HT_INK_WARM at the top of script.js, kept as a constant because the split reads best as a fixed print-shop pairing. Edit it there if your palette needs a different companion colour.

Options

Set on the [data-halftone] container.

Attribute Values Default Description
data-halftone-cell Number (px), 8 to 20 is sensible 12 Grid cell size. Smaller cells mean a finer print and more dots to draw; below 8px profile before shipping
data-halftone-ink Any CSS colour #4f74e3 Leading ink of the misregistration split, revealed on a fast whip
data-halftone-paper Any CSS colour #f1ede4 The dot colour, the "paper" the image is printed with
data-halftone-strength Number, 0.4 to 2 is sensible 1 Wake multiplier. Below 1 the print barely stirs; above 1 gentle passes already smear and tear

Markup Hooks

Attribute Where Required Description
data-halftone Container Yes Marks the stage; the script appends its canvas here
<img> Inside the container Yes The source pixels and the no-JS fallback
tabindex="0" Container Recommended Makes the stage focusable; focus replays the print sweep
role="img" + aria-label Container Recommended Describes the composition to assistive technology

Accessibility

  • Reduced motion: under prefers-reduced-motion: reduce the script renders the fully resolved halftone once as a still image. No print pass, no idle sweep, no wake, and never a blank stage.
  • No JavaScript: the real <img> is visible by default; the script hides it only after it has taken over. If GSAP or the image fails to load, the photograph returns.
  • Keyboard: with tabindex="0" the stage is focusable. Focusing it (via keyboard) runs one sweep pass across the print, and Enter or Space replays it.
  • Focus visible: the stage shows an accent outline under :focus-visible.
  • Labels: role="img" with a descriptive aria-label on the container, plus a real alt on the image, keeps the composition meaningful to screen readers.

Performance

  • One gsap.ticker callback drives sampling, springs and rendering; it idles while the tab is hidden and removes itself if the stage leaves the DOM.
  • Dot state lives in typed arrays (Float32Array per channel), never one object or tween per dot.
  • The whole grid renders in at most three canvas fills per frame: two batched ink paths and one paper path.
  • Rendering resolution is capped at 2x device pixel ratio.
  • The image is resampled only on resize (debounced), never per frame.
  • At the default 12px cell a full 1920x1080 stage is about 14,000 dots, which is comfortable. Very small cells on very large stages multiply quickly; check a mid-range phone before shipping below 8px.

Dependencies

Required:

  • GSAP 3.12+ (core only, no plugins)

Optional:

  • None

Image Credit

The bundled demo portrait derives from an Unsplash photograph (source: https://images.unsplash.com/photo-1506794778202-cad84cf45f1d).

Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.