3.3 KiB
Illustrated Manuscript
An interactive illuminated manuscript page featuring an animated dragon that follows your cursor, breathes fire on click, and causes text to flow around it in real-time.
Overview
This project renders a medieval manuscript page on Canvas, featuring a hand-painted dragon made of sprite segments. The dragon tracks the mouse cursor with chain-physics (20 body segments with angle constraints), and clicking makes it breathe fire. Text reflows around the dragon body and fire particles each frame using Pretext's layoutNextLine API — no DOM measurements, no CSS reflow.
Features
- Real-time text exclusion — text wraps around arbitrary moving shapes (dragon body + fire particles), re-laid out every frame via Pretext
- Chain-physics dragon — 20-segment inverse-kinematics body, hand-painted sprites, wing flapping, idle rest pose
- Fire-breathing — click to spawn particle bursts from the dragon's mouth; particles push and distort nearby text characters
- Illuminated drop cap — hand-drawn initial letter in manuscript tradition
- Medieval aesthetic — IM Fell English font, parchment tones, hand-painted sprites throughout
Tech Stack
- Pretext — Pure JS/TS text measurement & layout engine by Cheng Lou. Enables laying out text without DOM reflow, line by line with variable widths
- WebGL2 + 2D Canvas — offscreen 2D Canvas compositing, uploaded to WebGL2 texture for display
- Vite — build tool
Project Structure
src/
config.js — constants, responsive layout calculations
dragon.js — dragon physics, sprite rendering, fire particle system
story.js — manuscript story text
main.js — entry point: WebGL pipeline, input, text layout, render loop
public/
dragon-sprites/ — 23 hand-painted dragon body part PNGs
fire-sprites/ — 10 fire particle PNGs
img/dropcap.png — illuminated drop cap
(IM Fell English loaded via Google Fonts)
Getting Started
npm install
npm run dev
Open in browser. Desktop recommended — mobile support is limited.
How It Works
- Text preparation: story text is segmented and measured once via
prepareWithSegments() - Each frame: dragon segments and fire particles generate exclusion rectangles
- Text layout: for each line, available width ranges are computed by subtracting exclusion zones, then
layoutNextLine()fills each range - Fire text effect: characters near fire particles are displaced, rotated, and color-shifted based on proximity
- Rendering: everything composites on an offscreen 2D Canvas, then uploads to a WebGL2 fullscreen quad
Credits
Created by River Marchand (@Riyvir), Creative Director at Neither/Nor.
Built with Pretext by Cheng Lou — a pure JavaScript/TypeScript library for multiline text measurement & layout, enabling web UIs to bypass DOM measurements and reflow entirely.
"This little illuminated dragon is very happy about Pretext." — @Riyvir
License
ISC