A creative coding tool for DOM-free, high-performance text layout in browser demos.

Install

mkdir -p .claude/skills/pretext && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/16360" && unzip -o skill.zip -d .claude/skills/pretext && rm skill.zip

Installs to .claude/skills/pretext

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Use when building creative browser demos with @chenglou/pretext — DOM-free text layout for ASCII art, typographic flow around obstacles, text-as-geometry games, kinetic typography, and text-powered generative art. Produces single-file HTML demos by default.
257 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Advanced

Key capabilities

  • Measure multiline text layout without DOM
  • Reflow paragraphs around moving sprites at 60fps
  • Build games with text as level geometry
  • Shatter text into particles with per-grapheme positions
  • Pack shrink-wrapped multiline UI without `getBoundingClientRect` thrash

How it works

It uses canvas measurement to determine text layout properties like line breaks, widths, and positions, providing geometric data without relying on the DOM.

Inputs & outputs

You give it
(text, font, width)
You get back
line breaks, per-line widths, per-grapheme positions, and total height

When to use pretext

  • Building kinetic typography demos
  • Creating ASCII art games
  • Developing text-powered generative art
  • Optimizing text flow around shapes

About this skill

Pretext Creative Demos

Overview

@chenglou/pretext is a 15KB zero-dependency TypeScript library by Cheng Lou (React core, ReasonML, Midjourney) for DOM-free multiline text measurement and layout. It does one thing: given (text, font, width), return the line breaks, per-line widths, per-grapheme positions, and total height — all via canvas measurement, no reflow.

That sounds like plumbing. It is not. Because it is fast and geometric, it is a creative primitive: you can reflow paragraphs around a moving sprite at 60fps, build games whose level geometry is made of real words, drive ASCII logos through prose, shatter text into particles with exact per-grapheme starting positions, or pack shrink-wrapped multiline UI without any getBoundingClientRect thrash.

This skill exists so Hermes can make cool demos with it — the kind people post to X. See pretext.cool and chenglou.me/pretext for the community demo corpus.

When to Use

Use when the user asks for:

  • A "pretext demo" / "cool pretext thing" / "text-as-X"
  • Text flowing around a moving shape (hero sections, editorial layouts, animated long-form pages)
  • ASCII-art effects using real words or prose, not monospace rasters
  • Games where the playfield / obstacles / bricks are made of text (Tetris-from-letters, Breakout-of-prose)
  • Kinetic typography with per-glyph physics (shatter, scatter, flock, flow)
  • Typographic generative art, especially with non-Latin scripts or mixed scripts
  • Multiline "shrink-wrap" UI (smallest container width that still fits the text)
  • Anything that would require knowing line breaks before rendering

Don't use for:

  • Static SVG/HTML pages where CSS already solves layout — just use CSS
  • Rich text editors, general inline formatting engines (pretext is intentionally narrow)
  • Image → text (use ascii-art / ascii-video skills)
  • Pure canvas generative art with no text role — use p5js

Creative Standard

This is visual art rendered in a browser. Pretext returns numbers; you draw the thing.

  • Don't ship a "hello world" demo. The hello-orb-flow.html template is the starting point. Every delivered demo must add intentional color, motion, composition, and one visual detail the user didn't ask for but will appreciate.
  • Dark backgrounds, warm cores, considered palette. Classic amber-on-black (CRT / terminal) works, but so do cold-white-on-charcoal (editorial) and desaturated pastels (risograph). Pick one and commit.
  • Proportional fonts are the point. Pretext's whole vibe is "not monospaced" — lean into it. Use Iowan Old Style, Inter, JetBrains Mono, Helvetica Neue, or a variable font. Never default sans.
  • Real source/text, not lorem ipsum. The corpus should mean something. Short manifestos, poetry, real source code, a found text, the library's own README — never lorem ipsum.
  • First-paint excellence. No loading states, no blank frames. The demo must look shippable the instant it opens.

Stack

Single self-contained HTML file per demo. No build step.

LayerToolPurpose
Core@chenglou/pretext via esm.sh CDNText measurement + line layout
RenderHTML5 Canvas 2DGlyph rendering, per-frame composition
SegmentationIntl.Segmenter (built-in)Grapheme splitting for emoji / CJK / combining marks
InteractionRaw DOM eventsMouse / touch / wheel — no framework
<script type="module">
import {
  prepare, layout,                   // use-case 1: simple height
  prepareWithSegments, layoutWithLines,  // use-case 2a: fixed-width lines
  layoutNextLineRange, materializeLineRange, // use-case 2b: streaming / variable width
  measureLineStats, walkLineRanges,  // stats without string allocation
} from "https://esm.sh/@chenglou/[email protected]";
</script>

Pin the version. @0.0.6 at time of writing — check npm for the latest if demo behavior is off.

The Two Use Cases

Almost everything reduces to one of these two shapes. Learn both.

Use-case 1 — measure, then render with CSS/DOM

const prepared = prepare(text, "16px Inter");
const { height, lineCount } = layout(prepared, 320, 20);

You still let the browser draw the text. Pretext just tells you how tall the box will be at a given width, without a DOM read. Use for:

  • Virtualized lists where rows contain wrapping text
  • Masonry with precise card heights
  • "Does this label fit?" dev-time checks
  • Preventing layout shift when remote text loads

Keep font and letterSpacing exactly in sync with your CSS. The canvas ctx.font format (e.g. "16px Inter", "500 17px 'JetBrains Mono'") must match the rendered CSS, or measurements drift.

Use-case 2 — measure and render yourself

const prepared = prepareWithSegments(text, FONT);
const { lines } = layoutWithLines(prepared, 320, 26);
for (let i = 0; i < lines.length; i++) {
  ctx.fillText(lines[i].text, 0, i * 26);
}

This is where the creative work lives. You own the drawing, so you can:

  • Render to canvas, SVG, WebGL, or any coordinate system
  • Substitute per-glyph transforms (rotation, jitter, scale, opacity)
  • Use line metadata (width, grapheme positions) as geometry

For variable-width-per-line flow (text around a shape, text in a donut band, text in a non-rectangular column):

let cursor = { segmentIndex: 0, graphemeIndex: 0 };
let y = 0;
while (true) {
  const lineWidth = widthAtY(y);  // your function: how wide is the corridor at this y?
  const range = layoutNextLineRange(prepared, cursor, lineWidth);
  if (!range) break;
  const line = materializeLineRange(prepared, range);
  ctx.fillText(line.text, leftEdgeAtY(y), y);
  cursor = range.end;
  y += lineHeight;
}

This is the most important pattern in the whole library. It's what unlocks "text flowing around a dragged sprite" — the demo that went viral on X.

Helpers worth knowing

  • measureLineStats(prepared, maxWidth){ lineCount, maxLineWidth } — the widest line, i.e. multiline shrink-wrap width.
  • walkLineRanges(prepared, maxWidth, callback) — iterate lines without allocating strings. Use for stats/physics over graphemes when you don't need the characters.
  • @chenglou/pretext/rich-inline — the same system but for paragraphs mixing fonts / chips / mentions. Import from the subpath.

Demo Recipe Patterns

The community corpus (see references/patterns.md) clusters into a handful of strong patterns. Pick one and riff — don't invent a new category unless asked.

PatternKey APIExample idea
Reflow around obstaclelayoutNextLineRange + per-row width functionEditorial paragraph that parts around a dragged cursor sprite
Text-as-geometry gamelayoutWithLines + per-line collision rectsBreakout where each brick is a measured word
Shatter / particleswalkLineRanges → per-grapheme (x,y) → physicsSentence that explodes into letters on click
ASCII obstacle typographylayoutNextLineRange + measured per-row obstacle spansBitmap ASCII logo, shape morphs, and draggable wire objects that make text open around their actual geometry
Editorial multi-columnlayoutNextLineRange per column + shared cursorAnimated magazine spread with pull quotes
Kinetic typelayoutWithLines + per-line transform over timeStar Wars crawl, wave, bounce, glitch
Multiline shrink-wrapmeasureLineStatsQuote card that auto-sizes to its tightest container

See templates/donut-orbit.html and templates/hello-orb-flow.html for working single-file starters.

Workflow

  1. Pick a pattern from the table above based on the user's brief.
  2. Start from a template:
    • templates/hello-orb-flow.html — text reflowing around a moving orb (reflow-around-obstacle pattern)
    • templates/donut-orbit.html — advanced example: measured ASCII logo obstacles, draggable wire sphere/cube, morphing shape fields, selectable DOM text, and dev-only controls
    • write_file to a new .html in /tmp/ or the user's workspace.
  3. Swap the corpus for something intentional to the brief. Real prose, 10-100 sentences, no lorem.
  4. Tune the aesthetic — font, palette, composition, interaction. This is the work; don't skip it.
  5. Verify locally:
    cd <dir-with-html> && python3 -m http.server 8765
    # then open http://localhost:8765/<file>.html
    
  6. Check the console — pretext will throw if prepareWithSegments is called with a bad font string; Intl.Segmenter is available in every modern browser.
  7. Show the user the file path, not just the code — they want to open it.

Performance Notes

  • prepare() / prepareWithSegments() is the expensive call. Do it once per text+font pair. Cache the handle.
  • On resize, only rerun layout() / layoutWithLines() — never re-prepare.
  • For per-frame animations where text doesn't change but geometry does, layoutNextLineRange in a tight loop is cheap enough to do every frame at 60fps for normal-length paragraphs.
  • When rendering ASCII masks per frame, keep a cell buffer (Uint8Array/typed arrays), derive measured per-row obstacle spans from the cells or projected geometry, merge spans, then feed those spans into layoutNextLineRange before drawing text.
  • Keep visual animation and layout animation coupled. If a sphere morphs into a cube, tween both the rendered cell buffer and the obstacle spans with the same value; otherwise the demo looks painted-on instead of physically reflowed.
  • For fades, prefer layer opacity over changing glyph intensity or obstacle scale. Put transient ASCII sprites on their own canvas and fade the canvas with CSS/GSAP opacity so geometry does not appear to shrink.
  • Canvas ctx.font setting is surprisingly slow; set it once per frame if font doesn't vary, not per fillText call.

Common Pitfalls


Content truncated.

When not to use it

  • Static SVG/HTML pages where CSS handles layout
  • Rich text editors or general inline formatting engines
  • Image to text conversion (use `ascii-art` / `ascii-video` skills)

Limitations

  • Does not handle static SVG/HTML pages
  • Not suitable for rich text editors
  • Does not convert images to text

How it compares

This approach offers fast, DOM-free text measurement and layout, enabling dynamic and geometric text manipulations that are not feasible with standard CSS or `getBoundingClientRect` methods.

Compared to similar skills

pretext side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
pretext (this skill)03moReviewAdvanced
scroll-experience1016moNo flagsIntermediate
interaction-design155moNo flagsIntermediate
motion106moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

scroll-experience

davila7

Expert in building immersive scroll-driven experiences - parallax storytelling, scroll animations, interactive narratives, and cinematic web experiences. Like NY Times interactives, Apple product pages, and award-winning web experiences. Makes websites feel like experiences, not just pages. Use when: scroll animation, parallax, scroll storytelling, interactive story, cinematic website.

101142

interaction-design

wshobson

Design and implement microinteractions, motion design, transitions, and user feedback patterns. Use when adding polish to UI interactions, implementing loading states, or creating delightful user experiences.

1550

motion

onmax

Use when adding animations with Motion Vue (motion-v) - provides motion component API, gesture animations, scroll-linked effects, layout transitions, and composables for Vue 3/Nuxt

1044

frontend-enhancer

ailabs-393

This skill should be used when enhancing the visual design and aesthetics of Next.js web applications. It provides modern UI components, design patterns, color palettes, animations, and layout templates. Use this skill for tasks like improving styling, creating responsive designs, implementing modern UI patterns, adding animations, selecting color schemes, or building aesthetically pleasing frontend interfaces.

35

threejs-postprocessing

CloudAI-X

Three.js post-processing - EffectComposer, bloom, DOF, screen effects. Use when adding visual effects, color grading, blur, glow, or creating custom screen-space shaders.

17

animation-performance-retro

TheOrcDev

Optimize 8-bit animations for smooth performance. Apply when creating animated pixel art, game UI effects, or any retro-styled animations.

10

Search skills

Search the agent skills registry