# ASCII Focus

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

Canonical: https://gsapvault.com/effects/ascii-focus
Live demo: https://gsapvault.com/demos/ascii-focus/index.html

| Property | Value |
|----------|-------|
| Type | effect |
| Tier | paid |
| Price | £10 |
| Difficulty | advanced |
| Plugins | Core GSAP only |
| Techniques | webgl-shader, mouse-follow |
| Uses Lenis | No |

## Lighthouse, as measured

Google Lighthouse on the demo, 15 September 2026. A measurement of the demo as shipped, not a promise for your page.

| Category | Score |
|----------|-------|
| Accessibility | 100 |
| Best practices | 100 |

No performance score is published: it depends on the deployment (server compression, caching, CDN, connection and device) rather than on the code, so measure it where it will live.

## Overview

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.

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.

## Features

- 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

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

## Vibe-Code Ready Setup

This effect includes `START-HERE-AI.md`, a product-specific copy-paste setup prompt for Cursor, Claude Code, ChatGPT, GitHub Copilot, Windsurf, and other coding assistants. It tells the assistant to inspect the existing stack, integrate the supplied files, preserve the design, scope selectors, retain accessibility and responsive behaviour, add framework-appropriate GSAP cleanup, and report what it tested.

[How AI-assisted setup works](https://gsapvault.com/vibe-coding)

## How It Works

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.

## Documentation

How this effect works and how it goes into a page. The reference you use once you own the files (worked examples, events, the programmatic API, the class list) ships with the download.

### 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` | `4`–`20` | `8` | Resting character width in CSS pixels. Narrow layouts use 78% of this width. |
| `data-radius` | `0.15`–`1` | `0.58` | Maximum outer focus radius as a fraction of the shorter stage edge. |
| `data-ease` | `0.02`–`0.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.5`–`2` | `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

- Desktop: [JL Dixon — Close-up of the Face of a Tiger](https://www.pexels.com/photo/close-up-of-the-face-of-a-tiger-17110864/).
- Mobile: [Eterna Media — Majestic Bengal Tiger in Low Light](https://www.pexels.com/photo/majestic-bengal-tiger-in-low-light-36146550/).

Both are used under the [Pexels licence](https://www.pexels.com/license/). 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.

## What You Get

- `index.html`: working demo page
- `assets/script.js`: commented, readable source
- `assets/style.css`: effect styles
- `README.md`: full documentation with examples and framework integration notes
- `START-HERE-AI.md`: product-specific copy-paste prompt for AI-assisted setup
- `LICENSE.txt`: standard license terms
- Lifetime updates: re-download anytime from your library

## Get the Code

This is a premium effect. The standard license costs £10 one-time and covers unlimited personal and commercial projects with no attribution required for our code. Bundled third-party assets retain their own licences and attribution requirements. The only restrictions: no redistribution of the code itself and no competing effect libraries.

- [Buy ASCII Focus](https://gsapvault.com/effects/ascii-focus)
- [The Vault (£99 one-time, best value): every collection in the Vault library, plus future items added to those collections](https://gsapvault.com/effects)

---

From [GSAP Vault](https://gsapvault.com): production-ready GSAP animation effects. Full catalog for agents: https://gsapvault.com/llms-full.txt
