pretext
Skill41 starsChanged 7 months agoMITView on GitHub
Create text effects impossible with CSS alone — kinetic typography, flowing text around animated obstacles, calligrams, shrinkwrap chat bubbles, typographic ASCII art, glyph path animation, variable font waves, glyph morphing, illuminated manuscripts. Single-file HTML output, zero build step. Powered by @chenglou/pretext + opentype.js. Triggers on text effects, kinetic typography, text animation, calligram, glyph art, typography art.
We split this skill into the rules it gives an agent
21 rules. 0 are common, 2 are distinctive, and 19 are very specific to this project.
0
Common
Most of the 13 edited versions we read keep these.
2
Distinctive
This author's own choices. Worth a look.
19
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 74
Every design decision derives from the concept.
- Very specific: line 119
Import only the functions you need.
- Very specific: line 211
Never use system-ui as font — canvas and DOM can resolve different fonts on macOS
- Very specific: line 212
Never use React/Vue/framework — vanilla JS + HTML only
- Very specific: line 213
Never omit <meta name="viewport"> — proportional measurement depends on correct device pixels
- Very specific: line 214
Never use setInterval for animation — always requestAnimationFrame
- Distinctive: line 215
Never skip the resize handler — text layout depends on container width
- Very specific: line 216
Never call prepare() inside the animation loop — it's the expensive one-time pass.
- Very specific: line 217
Never omit document.fonts.ready.then(...) — measure after fonts load, not before
- Very specific: line 218
Never use pure black (#000000) — use rich off-blacks (#06060a, #08080e, #0a0a0c)
- Very specific: line 219
Never build line strings when you only need geometry — use walkLineRanges instead of layoutWithLines
- Very specific: line 220
Never position text with CSS flow — use position: absolute and place lines from Pretext coordinates
- Very specific: line 221
Never pick one side of an obstacle when the obstacle is in the middle — use carveTextLineSlots to fill both sides
- Very specific: line 222
Never create/destroy DOM elements per frame — use element pooling (syncPool)
- Very specific: line 225
Never use .woff2 files — opentype.js cannot parse them.
- Very specific: line 226
Never use opentype.load() — use fetch().then(r => r.arrayBuffer()).then(opentype.parse) for better error handling
- Very specific: line 227
Never render glyphs at absolute positions then try to move them — render at (0,0) and position via <g transform="translate(x,y)">
- Very specific: line 228
Never forget font.unitsPerEm — the scale factor is fontSize / font.unitsPerEm
- Very specific: line 229
Never call glyph.getPath() inside a tight loop without caching — cache the pathData string, regenerate only when position changes
- Very specific: line 230
Never skip kerning — always check font.getKerningValue(glyph, nextGlyph) * scale between adjacent glyphs
- Very specific: line 231
Never use Google Fonts TTF CDN URLs (they return 404 for programmatic access) — use @fontsource via jsdelivr
“Common” is measured: kept by at least 6 in 10 of the 13 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Pretext Text Effects
Generate browser pages powered by Pretext (@chenglou/pretext)—a pure-arithmetic text measurement library that bypasses DOM layout reflow entirely. Pretext measures text with proportional font precision using canvas measureText, enabling effects impossible with CSS alone.
Companion library: opentype.js (opentype.js@1.3.4) — direct font binary parsing for per-glyph SVG path rendering, Bezier control point access, kerning tables, and variable font axes. Pretext handles line breaking; opentype.js handles what happens inside each glyph. Together they unlock effects neither can achieve alone.
Output is always a single self-contained HTML file. No build step, no framework, runs in any modern browser.
Quick Start
Describe a text effect. Claude picks the right Pretext pattern and generates a complete HTML file.
Examples:
/pretext fluid smoke ASCII art with gold characters on black/pretext chat bubbles that shrinkwrap tighter than CSS/pretext calligram — the word "ocean" shaped like a wave/pretext editorial layout with text flowing around draggable circles/pretext masonry grid of shower thoughts with instant height prediction/pretext glyph path art — SVG letterforms with stroke animation/pretext animated dragon cursor that pushes text aside as it moves/pretext illuminated manuscript with live vine growth reflow/pretext variable font wave — per-character weight animation/pretext glyph morph — letterform interpolation from A to Z/pretext letterbox gallery of "BEACON" in Playfair Display italic on dark background/pretext glyph-mask calligram — letter R filled with lorem ipsum in Georgia
Do NOT Use This Skill When
- Simple CSS
text-shadow,text-stroke, or gradient text effects - CSS Shapes level 1 (
shape-outsideon floated elements) for static layouts - Basic
@keyframestext animation (fade, slide, typewriter) - Monospace ASCII art (no proportional measurement needed)
- SVG
<text>without per-glyph control - Rich text editing (
contenteditable, ProseMirror, TipTap) - PDF generation or markdown rendering
These are all achievable without Pretext or opentype.js.
Effect Categories
Pretext-Only Effects
| Category | Pretext APIs Used | When to Use |
| Height Prediction | prepare + layout | Accordion, masonry, virtualized lists — anywhere you need text height without DOM reads |
| Shrinkwrap | walkLineRanges + binary search | Chat bubbles, tooltips, labels — finding the exact tightest width for multiline text |
| Obstacle Routing | layoutNextLine (variable width) | Text flowing around images, logos, draggable orbs — editorial layouts |
| Animated Obstacles | layoutNextLine + carveTextLineSlots | Moving creatures/orbs that displace text at 60fps — slot-carving fills BOTH sides of obstacle |
| Typographic ASCII | prepareWithSegments (char measurement) | Fluid simulations, 3D wireframes, particle systems rendered as proportional characters |
| Calligrams | prepareWithSegments + SDF | Words rendered as shapes using their own letters — hearts, stars, spirals |
| Glyph-Mask Calligrams | Canvas measureText + getImageData pixel mask | Any font glyph as calligram shape — fill a large letter with small text using pixel-mask technique (no SDF needed, no opentype.js) |
| Letterbox Gallery | Glyph-mask + per-letter <canvas> grid | Each character in a string gets its own canvas with text fill, cursor displacement, and independent interaction |
| Multi-column Editorial | All rich APIs combined | Magazine-style layouts with headline fitting, pull quotes, drop caps, column flow |
Pretext + opentype.js Effects
| Category | APIs Used | When to Use |
| Glyph Path Art | opentype glyph.getPath() | SVG letterforms with fill/stroke/control point modes, stroke-draw animation |
| Text on Path | opentype getPath() + arc-length sampling | Per-glyph placement along Bezier curves with tangent rotation |
| Variable Font Animation | opentype font.tables.fvar.axes + CSS font-variation-settings | Per-character weight/width waves, breathe, ripple, cascade effects |
| Glyph Morphing | opentype paths + flubber interpolate() | Letterform interpolation between glyphs with contour-aware morphing |
| Outline Calligrams | Pretext layoutNextLine + opentype glyph mask | Text fills the interior of a large glyph's actual contour (not SDF approximation) |
| Illuminated Manuscript | All Pretext + opentype combined | Living medieval pages: wet ink, vine reflow, capital inflation, aging, erasure poetry |
Concept-to-Effect
Every design decision derives from the concept. Do not default—derive.
Choose the API tier from the effect complexity:
- Static height only →
prepare()+layout()(fastest, opaque) - Need line text/positions →
prepareWithSegments()+layoutWithLines() - Need per-line width variation →
layoutNextLine()(iterator, variable width per line) - Need aggregate geometry without strings →
walkLineRanges()(no string materialization) - Need individual character widths →
prepareWithSegments()on single chars
Choose the rendering target from the output type:
- DOM elements (
.line { position: absolute }) → editorial, accordion, masonry - Canvas 2D (
ctx.fillText) → calligrams, some ASCII art - HTML spans with inline styles → typographic ASCII (weight/style/opacity per character)
Architecture
Pretext CDN → prepare/prepareWithSegments → layout/layoutNextLine → line positions
↓
opentype.js CDN → font.parse(buffer) → glyph.getPath() ──────────> SVG <path> elements
↓
requestAnimationFrame (if animated)
resize handler (always)Two-library split: Pretext decides WHERE text goes (line breaking, obstacle routing). opentype.js decides HOW each glyph looks (SVG paths, Bezier curves, contour data). Use Pretext alone for DOM-positioned text. Add opentype.js when you need per-glyph SVG rendering, path animation, or glyph contour access.
CDN Imports
<!-- Pretext (ESM, required) -->
<script type="module">
import { prepare, layout, prepareWithSegments, layoutWithLines, walkLineRanges, layoutNextLine, clearCache } from 'https://esm.sh/@chenglou/pretext@0.0.2'
</script>
<!-- opentype.js (UMD, optional — only for glyph path effects) -->
<script src="https://cdn.jsdelivr.net/npm/opentype.js@1.3.4/dist/opentype.min.js"></script>
<!-- Font binary for opentype.js (Inter .woff — confirmed working) -->
<!-- const FONT_URL = 'https://cdn.jsdelivr.net/npm/@fontsource/inter@5.0.8/files/inter-latin-400-normal.woff' -->
<!-- flubber (optional — only for glyph morphing) -->
<!-- <script src="https://cdn.jsdelivr.net/npm/flubber@0.4.2/build/flubber.min.js"></script> -->Import only the functions you need. Pin the version. opentype.js cannot parse .woff2 — use .woff or .ttf only.
Composition Parameters
All Effects
| Parameter | Default | Notes |
| Font | '18px Georgia, Palatino, serif' | Never use system-ui — unreliable with Pretext |
| Line height | 28 (px) | Must match CSS line-height for height prediction |
| Background | #08080e or #f5f1ea | Dark or light — never pure black #000 |
Typographic ASCII
| Parameter | Range | Default |
| Font size | 10–18px | 14 |
| Font family | serif preferred | Georgia, Palatino, "Times New Roman", serif |
| Charset | printable ASCII | .,:;!+-=*#@%&a-zA-Z0-9 |
| Weights | 1–3 | [300, 500, 800] |
| Styles | normal, italic | both |
| Opacity levels | 6–10 | 10 CSS classes .a1–.a10 |
Calligrams
| Parameter | Range | Default |
| Canvas size | 200–800px | 400 |
| Char density | 6–24px | 14 |
| Shapes | heart, circle, star, wave, spiral | heart |
| Animation | spring entrance | springK: 0.08, damping: 0.75 |
Editorial / Obstacle Routing
| Parameter | Range | Default |
| Columns | 1–4 | 2 |
| Column gap | 20–60px | 40 |
| Gutter | 30–80px | 48 |
| Orb count | 1–6 | 5 |
| Orb radius | 30–120px | 65–110 |
| hPad (horizontal padding around obstacle) | 8–20px | 14 |
| vPad (vertical padding) | 2–8px | 4 |
| MIN_SLOT_WIDTH | 30–80px | 50 |
opentype.js — Glyph Path Effects
| Parameter | Range | Default |
| Font URL | .woff or .ttf only | @fontsource/inter@5.0.8 |
| Font size | 10–200px | 14 (body), 42 (display) |
| Stroke-dashoffset draw speed | 30–150ms per glyph | 80ms |
| Wet ink amplitude | 0.5–3px | 1.8 |
| Wet ink decay (tau) | 800–3000ms | 1500 |
| Glyph morph easing | linear, ease-in-out, spring | ease-in-out |
Glyph-Mask Calligrams / Letterbox Gallery
| Parameter | Range | Default |
| Canvas size (per letter) | 200–1000px | 500 |
| Fill font size | 8–24px | 11 |
| Fill model | self, lorem, alphabet, custom | lorem |
| Fill case | upper, lower, mixed | upper |
| Fill columns | 1–4 | 1 |
| Grid columns (gallery) | 2–6 | 3 |
| Cursor displacement radius | 50–250px | 100 |
| Cursor displacement force | 10–50 | 35 |
| Displacement damping | 0.85–0.98 | 0.94 |
opentype.js + flubber — Glyph Morphing
| Parameter | Range | Default |
| Morph duration | 300–2000ms | 800 |
| Flubber maxSegmentLength | 5–20 | 10 |
| Contour strategy | equal, 1-to-many, many-to-1 | auto-detect |
References
| Working on... | Load |
| Full API surface, types, caveats | references/api-reference.md |
| Typographic palette, brightness, ASCII grid | references/typographic-ascii.md |
| Column flow, obstacles, headline fitting, slot-carving, animated physics | references/obstacle-routing.md |
| SDF shapes, proportional spacing, animation | references/calligram-shapes.md |
| Pixel-mask technique, glyph-mask calligrams, cursor displacement, letterbox gallery | references/calligram-shapes.md (Pixel-Mask section) |
| opentype.js + Pretext integration patterns | references/opentype-integration.md |
| Working accordion template | assets/templates/height-prediction.html |
| Working bubbles template | assets/templates/shrinkwrap-bubbles.html |
| Working editorial template | assets/templates/editorial-engine.html |
| Working ASCII art template | assets/templates/typographic-ascii.html |
| Working calligram template | assets/templates/calligram.html |
| Working letterbox gallery template | assets/templates/letterbox-gallery.html |
| GlyphKit demos (6 working demos, local) | ~/Desktop/Programming/glyphkit/demos/ (machine-specific) |
Anti-Patterns
Pretext
- Never use
system-uias font — canvas and DOM can resolve different fonts on macOS - Never use React/Vue/framework — vanilla JS + HTML only
- Never omit
<meta name="viewport">— proportional measurement depends on correct device pixels - Never use
setIntervalfor animation — alwaysrequestAnimationFrame - Never skip the resize handler — text layout depends on container width
- Never call
prepare()inside the animation loop — it's the expensive one-time pass. Cache it. - Never omit
document.fonts.ready.then(...)— measure after fonts load, not before - Never use pure black (
#000000) — use rich off-blacks (#06060a,#08080e,#0a0a0c) - Never build line strings when you only need geometry — use
walkLineRangesinstead oflayoutWithLines - Never position text with CSS flow — use
position: absoluteand place lines from Pretext coordinates - Never pick one side of an obstacle when the obstacle is in the middle — use
carveTextLineSlotsto fill both sides - Never create/destroy DOM elements per frame — use element pooling (
syncPool)
opentype.js
- Never use
.woff2files — opentype.js cannot parse them. Use.woffor.ttfonly - Never use
opentype.load()— usefetch().then(r => r.arrayBuffer()).then(opentype.parse)for better error handling - Never render glyphs at absolute positions then try to move them — render at
(0,0)and position via<g transform="translate(x,y)"> - Never forget
font.unitsPerEm— the scale factor isfontSize / font.unitsPerEm - Never call
glyph.getPath()inside a tight loop without caching — cache the pathData string, regenerate only when position changes - Never skip kerning — always check
font.getKerningValue(glyph, nextGlyph) * scalebetween adjacent glyphs - Never use Google Fonts TTF CDN URLs (they return 404 for programmatic access) — use
@fontsourcevia jsdelivr
Post-Generation QA
python3 ~/.claude/skills/pretext/scripts/validate_pretext.py output.htmlChecklist:
- ESM import from
esm.sh/@chenglou/pretextpresent <script type="module">tag- Named font declaration (no
system-ui) <meta name="viewport">present- Window resize handler
document.fonts.readyawaited before measurementrequestAnimationFrameloop (for animated effects)- Touch event handlers (for interactive effects)
- No
setIntervalfor animation - No framework imports
Other versions14 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 14
- aivrar/portable-hermes-agent★ 239same name
- delorenj/skills★ 18same name
- AsharibAli/pretext-skills★ 12same name
- Samin12/skills★ 8same name
- john-data-chen/hermes-agent-backup★ 2same name
- ekajaya740/skills★ 1same name
- New1Direction/pretexter★ 1same name
- pedroiff0/awesome-skills★ 1same name
- Stuko0/alice-agent★ 1same name
- ishanbhosale08/Daedalus★ 0same name
4 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
Used alongsideProjects with it also use touchdesigner-mcp, merge-reconciler and 10 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
john-data-chenSkilltouchdesigner-mcp
Control a running TouchDesigner instance via twozero MCP — create operators, set parameters, wire connections, execute Python, build real-time visuals. 36 native tools.
Also in 7 projects that use this
aivrarSkillmerge-reconciler
Neutral third-party resolution of agent merge conflicts.
Also in 3 projects that use this
math-incSkillheartmula
Set up and run HeartMuLa, the open-source music generation model family (Suno-like). Generates full songs from lyrics + tags with multilingual support.
Also in 6 projects that use this
Tommy-ywSkillpopular-web-designs
54 production-quality design systems extracted from real websites. Load a template to generate HTML/CSS that matches the visual identity of sites like Stripe, Linear, Vercel, Notion, Airbnb, and more. Each template includes colors, typography, components, layout rules, and ready-to-use CSS values.
Also in 9 projects that use this
mae616Skillp5js
p5.js(クリエイティブコーディング)のProcessing由来「コードでスケッチ」思想を軸に、setup/drawループ・座標系・インタラクション・パフォーマンス最適化を整理する。p5.js、Processing、クリエイティブコーディング、ジェネラティブアート、インタラクティブ表現、ビジュアルプログラミング、Canvas描画、パーティクル、フラクタル、ノイズ表現に関する相談で必ず使うこと。ユーザーが「コードでアート」「動くビジュアル」「インタラクティブな描画」と言った場合にも適用する。
Also in 9 projects that use this
john-data-chenSkillteams-meeting-pipeline
Operate the Teams meeting summary pipeline via Hermes CLI — summarize meetings, inspect pipeline status, replay jobs, manage Microsoft Graph subscriptions.
Also in 6 projects that use this
In this repository99 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- agents-md-managerSkill
- architecture-md-builderSkill
- autoresearchSkill
- claude-agent-sdkSkill
- claude-md-managerSkill
- claude-usageSkill
- dag-typesafeSkill
- fableSkill
- glossarySkill
- openrouter-usageSkill
- react-best-practicesSkill
- skill-optimizerSkill
- stop-slopSkill
- tamarruSkill
- test-harness-auditorSkill
- user-model-builderSkill
- atmosphere-shaderSkill
- component-gallerySkill
- conductor-motionSkill
- design-auditSkill
- design-critiqueSkill
- design-mdSkill
- design-polishSkill
- gemini-claude-resonanceSkill
- gemini-forgeSkill
- gpt-atelierSkill
- grainientSkill
- image-forgeSkill
- image-wellSkill
- meshySkill
- mindnode-uiSkill
- minoan-frontend-designSkill
- nano-banana-proSkill
- overwatch-slidedeckSkill
- paper-designSkill
- particle-swarm-simSkill
- pellicolaSkill
- photo-to-slack-emojiSkill
- recordlySkill
- rocaille-shaderSkill
- scroll-cinemaSkill
- shadcnSkill
- shapeSkill
- smolvlmSkill
- speak-responseSkill
- sprite-forgeSkill
- threejs-particle-canvasSkill
- vellum-editorialSkill
- webgpu-threejs-tslSkill
- agent-browserSkill
- beautiful-mermaidSkill
- classical-887Skill
- claude-peersSkill
- cloudflareSkill
- codex-ctoSkill
- codex-orchestratorSkill
- disc-forgeSkill
- figma-mcpSkill
- linkedin-exportSkill
- The first 60 shown.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

