Glyph Field

ASCII Art Effect for React

Your mark drawn in type. A word, an SVG path or a photo is printed as a grid of monospace glyphs that run from bare paper through dots to a dense @. It prints left to right the first time it scrolls into view, and the pointer inks the glyphs it passes over in your accent, which then dry back to their resting weight.

  • Canvas
  • Hero
  • Hover
  • Text

Uses motion, clsx, tailwind-merge

Open in ChatGPTOpen in Claude
cellSize12px
decodetrue(replays the demo)
drifttrue
printDuration0.75s(replays the demo)
dry0.42s
<GlyphField />

replays the demo when you let go.

NewRemote cache for coding agents
Every build your agents run, already cached.

Halcyon shares one build cache across your team and every coding agent, so the rebuild that took eleven minutes lands in four seconds.

Take this demo to your coding agent.
Add this moment from KeyframeUI to our project and adapt it to our content.

Glyph Field: Your mark drawn in type. A word, an SVG path or a photo is printed as a grid of monospace glyphs that run from bare paper through dots to a dense @. It prints left to right the first time it scrolls into view, and the pointer inks the glyphs it passes over in your accent, which then dry back to their resting weight.
This demo, Orbit Hero: A dev tool's hero with no source set, so the field prints its lit orbit around the headline. Move across it and the glyphs ink violet and dry back.

1. Install it:
   npx shadcn@latest add @keyframeui/glyph-field-demo
   (If the CLI can't find @keyframeui, use https://keyframeui.com/r/glyph-field-demo.json instead.)
   It adds the demo, components/glyph-field-demo.tsx, and the component, components/ui/glyph-field.tsx.

2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <GlyphField> on its own.

3. These props set its feel. Change them before you change the file:
   - cellSize (default 12): Font size of the glyphs, in px. Each cell is one glyph wide and 1.2× this tall; smaller cells keep more detail.
   - decode (default true): Print the mark left to right, with a band of flickering glyphs at the print head, the first time it scrolls into view.
   - drift (default true): Let a few glyphs slip a step lighter or heavier and back at idle, so the mark looks alive.
   - printDuration (default 0.75): How long the print head takes to cross the mark the first time it prints, in seconds.
   - dry (default 0.42): How quickly the ink dries back to the resting glyphs, in seconds. It's a time constant, so the ink is gone after about three times this.

4. Don't change, in components/ui/glyph-field.tsx:
   - Each glyph and color step is drawn once into an atlas, and a frame redraws only the cells whose glyph or shade changed, each with a single `drawImage`.
   - One rAF loop runs only while something moves (the print, the brush, wet ink) and stops off screen, on a hidden tab or once the ink has dried; drift runs on a slow interval instead.
   - The print runs once per mark, the first time it scrolls into view; a resize resets the grid without reprinting.
   - The brush lands where the pointer enters instead of gliding in, and stamps along its path so a fast stroke stays unbroken.
   - Under reduced motion it prints at once with no drift, the brush follows the pointer exactly, and ink dries on the short `dryReduced`.
   Its timing is all in the TIMING block at the top of that file.

Docs: https://keyframeui.com/components/glyph-field.md

Installation

Add the component on its own with the CLI, or copy the file in by hand.

The CLI writes components/ui/glyph-field.tsx and installs motion, clsx and tailwind-merge. It needs the @keyframeui registry in components.json; add it once per project.

npx shadcn@latest add @keyframeui/glyph-field

Examples

Finished sections built with Glyph Field. Each one installs whole, with the component included.

Not Found

A docs site's 404 with the number printed from `text`, soft edges shading off into dots, and a way back to the page that moved.

GET /docs/webhooks/retries → 404

This page was never printed

Retries moved into the delivery guide in Tinta 3.2. The link you followed points at the old address.

Take this demo to your coding agent.
Add this moment from KeyframeUI to our project and adapt it to our content.

Glyph Field: Your mark drawn in type. A word, an SVG path or a photo is printed as a grid of monospace glyphs that run from bare paper through dots to a dense @. It prints left to right the first time it scrolls into view, and the pointer inks the glyphs it passes over in your accent, which then dry back to their resting weight.
This demo, Not Found: A docs site's 404 with the number printed from `text`, soft edges shading off into dots, and a way back to the page that moved.

1. Install it:
   npx shadcn@latest add @keyframeui/glyph-field-404-demo
   (If the CLI can't find @keyframeui, use https://keyframeui.com/r/glyph-field-404-demo.json instead.)
   It adds the demo, components/glyph-field-404-demo.tsx, and the component, components/ui/glyph-field.tsx.

2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <GlyphField> on its own.

3. These props set its feel. Change them before you change the file:
   - cellSize (default 12): Font size of the glyphs, in px. Each cell is one glyph wide and 1.2× this tall; smaller cells keep more detail.
   - decode (default true): Print the mark left to right, with a band of flickering glyphs at the print head, the first time it scrolls into view.
   - drift (default true): Let a few glyphs slip a step lighter or heavier and back at idle, so the mark looks alive.
   - printDuration (default 0.75): How long the print head takes to cross the mark the first time it prints, in seconds.
   - dry (default 0.42): How quickly the ink dries back to the resting glyphs, in seconds. It's a time constant, so the ink is gone after about three times this.

4. Don't change, in components/ui/glyph-field.tsx:
   - Each glyph and color step is drawn once into an atlas, and a frame redraws only the cells whose glyph or shade changed, each with a single `drawImage`.
   - One rAF loop runs only while something moves (the print, the brush, wet ink) and stops off screen, on a hidden tab or once the ink has dried; drift runs on a slow interval instead.
   - The print runs once per mark, the first time it scrolls into view; a resize resets the grid without reprinting.
   - The brush lands where the pointer enters instead of gliding in, and stamps along its path so a fast stroke stays unbroken.
   - Under reduced motion it prints at once with no drift, the brush follows the pointer exactly, and ink dries on the short `dryReduced`.
   Its timing is all in the TIMING block at the top of that file.

Docs: https://keyframeui.com/components/glyph-field.md

Studio Team

A studio's team cards with each portrait printed from a photo. `tone="auto"` keeps the white backdrops as paper in both color schemes, and the brush inks orange.

The studio, in type

Field & Fold · Lisbon

  • Ines Duarte

    Creative director

    Twelve years of identities, two of them for orchestras.

  • Jonah Pike

    Motion lead

    Builds every brand a way to move before it gets a logo.

  • Mara Lindqvist

    Type and editorial

    Draws the custom faces. Owns the studio's only Linotype.

Take this demo to your coding agent.
Add this moment from KeyframeUI to our project and adapt it to our content.

Glyph Field: Your mark drawn in type. A word, an SVG path or a photo is printed as a grid of monospace glyphs that run from bare paper through dots to a dense @. It prints left to right the first time it scrolls into view, and the pointer inks the glyphs it passes over in your accent, which then dry back to their resting weight.
This demo, Studio Team: A studio's team cards with each portrait printed from a photo. `tone="auto"` keeps the white backdrops as paper in both color schemes, and the brush inks orange.

1. Install it:
   npx shadcn@latest add @keyframeui/glyph-field-portrait-demo
   (If the CLI can't find @keyframeui, use https://keyframeui.com/r/glyph-field-portrait-demo.json instead.)
   It adds the demo, components/glyph-field-portrait-demo.tsx, and the component, components/ui/glyph-field.tsx.

2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <GlyphField> on its own.

3. These props set its feel. Change them before you change the file:
   - cellSize (default 12): Font size of the glyphs, in px. Each cell is one glyph wide and 1.2× this tall; smaller cells keep more detail.
   - decode (default true): Print the mark left to right, with a band of flickering glyphs at the print head, the first time it scrolls into view.
   - drift (default true): Let a few glyphs slip a step lighter or heavier and back at idle, so the mark looks alive.
   - printDuration (default 0.75): How long the print head takes to cross the mark the first time it prints, in seconds.
   - dry (default 0.42): How quickly the ink dries back to the resting glyphs, in seconds. It's a time constant, so the ink is gone after about three times this.

4. Don't change, in components/ui/glyph-field.tsx:
   - Each glyph and color step is drawn once into an atlas, and a frame redraws only the cells whose glyph or shade changed, each with a single `drawImage`.
   - One rAF loop runs only while something moves (the print, the brush, wet ink) and stops off screen, on a hidden tab or once the ink has dried; drift runs on a slow interval instead.
   - The print runs once per mark, the first time it scrolls into view; a resize resets the grid without reprinting.
   - The brush lands where the pointer enters instead of gliding in, and stamps along its path so a fast stroke stays unbroken.
   - Under reduced motion it prints at once with no drift, the brush follows the pointer exactly, and ink dries on the short `dryReduced`.
   Its timing is all in the TIMING block at the top of that file.

Docs: https://keyframeui.com/components/glyph-field.md

Props

text

Typestring

A word or short line to print, set at weight 800. Use \n for a second line.

textFont

TypestringDefault"ui-sans-serif, system-ui, sans-serif"

Font family text is set in before it's turned into glyphs.

path

Typestring

An SVG path (d) to print, such as your logo mark. Read against viewBox.

viewBox

TypestringDefault"0 0 24 24"

The coordinate box path is drawn in, like an SVG's viewBox.

src

Typestring

An image to print, mapped by tone. It must be served with CORS headers (Unsplash is). With no text, path or src, it prints a lit orbit.

tone

Type"auto" | "dark" | "light"Default"auto"

For src: which tones print heavy. auto reads the backdrop from the photo's edges and prints what differs from it (dark tones on a light backdrop, light tones on a dark one), so the subject comes up as ink and the backdrop stays paper in either color scheme.

fit

Type"contain" | "cover"Default"contain"

contain fits the whole mark inside the field; cover fills the field and crops.

cellSize

TypenumberDefault12

Font size of the glyphs, in px. Each cell is one glyph wide and 1.2× this tall; smaller cells keep more detail.

glyphs

TypestringDefault" .:-=+*#%@"

The glyphs from lightest to densest. Start with a space so the paper stays bare.

softness

TypenumberDefault0.4

For text and path: how far the edges shade off into dots, from 0 (hard) to 1 (soft).

ink

Typeboolean | numberDefaulttrue

The pointer inks the glyphs it passes over, which dry back over about a second. A number sets the brush radius in px (88 by default). Touch inks on tap and drag.

decode

TypebooleanDefaulttrue

Print the mark left to right, with a band of flickering glyphs at the print head, the first time it scrolls into view.

drift

TypebooleanDefaulttrue

Let a few glyphs slip a step lighter or heavier and back at idle, so the mark looks alive.

printDuration

TypenumberDefault0.75

How long the print head takes to cross the mark the first time it prints, in seconds.

dry

TypenumberDefault0.42

How quickly the ink dries back to the resting glyphs, in seconds. It's a time constant, so the ink is gone after about three times this.

label

Typestring

Describes the mark for screen readers. Without it, the field is decorative and hidden from them.

glyphClassName

TypestringDefault"text-zinc-500 dark:text-zinc-400"

A text-* color for the glyphs. The canvas reads it, so dark: variants work; lighter glyphs are drawn fainter.

accentClassName

TypestringDefault"text-violet-600 dark:text-violet-400"

A text-* color the ink takes.

canvasClassName

Typestring

Classes for the canvas. It fills the field by default; position it to print the mark in one part only. Its font (font-mono) is the glyphs' font.

children

TypeReact.ReactNode

Content drawn over the field, like a headline. The pointer still inks through it.

className

Typestring

Classes for the field. Give it a size (a height, an aspect ratio, or absolute inset-0 inside a section).