092

ASCII Focus

£10

A photographic character field that resolves into finer glyphs and a clear image as a focus lens moves across it.

advanced
2 more details
webgl-shadermouse-follow
ASCII Focus - GSAP animation effect preview

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

About this effect

A photograph rebuilt from fine ASCII characters, with a movable focus area that steps through denser character grids before resolving into the original image. Fast sweeps leave a short, stepped wake that settles back into the character field.

Read the full effect overview

The colour treatment comes from the photograph itself. An Ink toggle switches to warm monochrome, and the focus lens works with a pointer, touch or keyboard.

What's included

8 items
  • Real GLSL character rendering from a luminance-selected glyph atlas
  • Three discrete density rings surrounding an original-image focus core
  • A short velocity-sensitive wake after quick sweeps and reversals
  • Original-image colour and warm monochrome Ink modes
  • Touch interaction, optional phone drift and keyboard arrow positioning
  • Responsive local imagery and an editable photo source
  • Styled source-image fallback for reduced motion, unavailable WebGL and no JavaScript
  • GSAP radius and colour transitions with complete renderer teardown

Perfect for

4 use cases
  • Photography portfolios with an exploratory image reveal
  • Editorial art direction that mixes digital type and documentary imagery
  • Music and creative campaigns with a tactile character treatment
  • Product photography that reveals detail beneath an abstract surface

How it works

1 section

Three.js draws a full-screen shader that reconstructs the photograph from a character atlas. The focus changes the level of detail, and pointer velocity adds a brief wake. GSAP animates the focus radius and colour blend and manages motion preferences and teardown; the render loop owns continuous pointer following and wake decay.

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

Lighthouse, as measured

Google Lighthouse on this effect's demo, 15 September 2026. 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 ASCII Focus 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.

£10 Standard license, unlimited projects

Browse free effects

Documentation

Quick Start

Copy assets/style.css, assets/script.js and your images into your project. Keep the source photograph in the HTML: it is also the fallback for reduced motion, unavailable WebGL and no JavaScript.

1. In the document head, before the stylesheet:

Code snippet omitted: it ships with the download.

The synchronous probe prevents the source photograph flashing before the character canvas appears. The timeout restores the photograph if a required script never loads.

2. Add the image and optional controls:

Code snippet omitted: it ships with the download.

3. Before the closing body tag:

Code snippet omitted: it ships with the download.

Serve the files through your local development server. Opening the HTML as a file:// URL can prevent texture loading. If your application already imports Three.js, assign that module to window.THREE before loading script.js and use your existing import map; do not load a second copy.

Using It With Your Own Design

Required markup: one [data-ascii-focus] root with a nonzero width and height, containing a [data-ascii-source] picture element and an image. The script reads that decoded image, including the currently selected responsive source. Each root gets an independent renderer and controls. Keep the image's useful alt text in the DOM; the generated canvas is hidden from assistive technology.

Optional controls: [data-ascii-position] is a native button. Clicking it moves to another focal point; with keyboard focus, the arrow keys move the lens freely. [data-ascii-colour] is a native Ink toggle with aria-pressed. Both can be omitted without affecting pointer input. Their styling and visible labels are yours to change.

Required CSS: keep the root relatively positioned and clipped, the canvas and source absolutely fitted to it, touch-action: pan-y, and the html.gl / .is-live source visibility gates. The source should remain a composed object-fit: cover photograph before JavaScript. Keep the reduced-motion rules and visible keyboard focus.

Demo styling: the full-viewport height, monospace controls, black button surfaces, spacing and cue are removable. When embedding in a normal page, remove the demo's global body { overflow: hidden; } and replace .ascii-stage { height: 100svh; } with your intended section height. A ResizeObserver measures the root, so the shader follows layout changes as well as browser resizing.

Options

Place these attributes on [data-ascii-focus] before initialisation. Explicit zero values are supported where listed.

Attribute Values Default Purpose
data-cell 420 8 Resting character width in CSS pixels. Narrow layouts use 78% of this width.
data-radius 0.151 0.58 Maximum outer focus radius as a fraction of the shorter stage edge.
data-ease 0.020.6 0.16 Pointer follow fraction at 60Hz, converted to elapsed-time decay for other refresh rates.
data-colour 0, 1 1 Initial original-image colour; 0 starts in Ink mode.
data-auto 0, 1 1 Gentle focus drift on coarse pointers after three seconds without input. 0 disables it.
data-pixel-ratio 0.52 2 fine / 1.5 coarse Renderer resolution cap, also limited by device pixel ratio.

Mouse pointer leave returns the radius to 68% of its active size. The picture remains made from characters at rest. Movement, focusing the Focus button or using an arrow key restores the full radius.

Choosing Your Images

The character grid needs a recognisable silhouette, strong midtone-to-shadow boundaries and local texture. Faces, fur, carved objects and sharply lit architecture are good candidates. Blurred skies, soft gradients and small subjects far from the camera lose their identity when reduced to glyphs. Keep the feature people should explore comfortably inside the frame; the shader uses a centred cover crop.

Use a detailed 1600–2048px image for a large desktop stage. The demo includes a 2048px landscape tiger face and a separate 1280px portrait profile in a <source media="(max-width: 599px)">. This is deliberate mobile art direction: replace both sources or remove the source element to use the same photograph everywhere. Do not sharpen or pre-darken an image heavily; the shader handles character brightness and colour.

The texture is made from the decoded HTML image, so there is no duplicate image fetch. An image's load event updates the texture when a responsive source changes. Use a local bundled image or a correctly CORS-enabled source.

Image Credits

Both are used under the Pexels licence. They are illustrative demo photography; no endorsement is implied. The baked images are in assets/img/, with the original source records in assets/img-src/SOURCES.md. Replace the WebP files directly, or edit assets/img-manifest.json and run the repository's bun scripts/build-template-assets.ts ascii-focus to create new derivatives. The image manifest intentionally applies no grade because the shader shades the images itself.

Accessibility

  • The source image and its alt text remain in the document. The canvas is decorative to assistive technology.
  • The native Focus button supports arrow keys, Enter and Space; Ink exposes its pressed state.
  • Touch moves the same focus lens as a mouse. Horizontal drags work without disabling vertical page scrolling.
  • Reduced motion shows the static source photograph and creates no WebGL canvas. Changing the preference while the effect is mounted disposes or rebuilds the effect immediately.
  • No JavaScript, blocked dependencies and unavailable WebGL retain the same styled source image. Interactive controls appear only after the canvas is ready.

Events and Programmatic Control

The root fires asciiready after the image has decoded and the initial radius transition has finished. The same state is exposed as data-ready="true".

Code snippet omitted: it ships with the download.

root.asciiFocus.state() exposes readiness, focus position/target, radius in pixels, wake energy, colour blend, selected image URL, dimensions and loop status. freeze(x, y, radiusFactor = 1) stops input drift for a deterministic capture at a fixed point; new pointer input resumes normal operation. Disable the optional drift with data-auto="0" for repeatable interaction recordings. render() synchronously draws the current frame for canvas-pixel checks.

To remove one instance:

Code snippet omitted: it ships with the download.

To remove all instances created by this script and their media-query listeners:

Code snippet omitted: it ships with the download.

Use revert(), not context kill(). Teardown removes input and visibility listeners, observers and animation frames, reverts owned tweens, disposes the atlas/image textures, geometry, material and renderer, releases the WebGL context and restores the source image. Mount the script again when creating a fresh view.

Browser Support

The animated treatment requires a browser with WebGL2, JavaScript modules and import maps. The source photograph is available in browsers that do not support the animated path. A lost GPU context restores the source image until the renderer is recovered.

Performance

One unlit fullscreen plane and one small generated character atlas render the treatment. No DOM character grid, postprocessing render targets or frame-by-frame text rasterisation are involved. The loop pauses when the root leaves the viewport or the document becomes hidden.

For a busy page or a less powerful phone, first lower data-pixel-ratio to 1. Keep the photograph near its actual display size. The coarse-pointer default is capped at 1.5. Software-rendered headless captures prove the output exists; they are not a hardware performance benchmark.

Dependencies

Dependency Demo version Responsibility
GSAP core 3.15.0 Focus-radius and colour transitions, initial radius settling, media-query lifecycle and tween cleanup. Compatible with GSAP 3.12+.
Three.js 0.180.0 WebGL2 renderer, image/atlas textures and shader material.

The shader owns the character reconstruction, discrete density rings and image core. The requestAnimationFrame loop owns pointer chase and wake decay. GSAP does not draw or animate individual glyphs. No GSAP plugin or Lenis is required.

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

Your cart

Your cart is empty

The Vault £99

The Vault library, plus future additions to the library.