Inflating word
- Source
- arlan.me/vault ↗
- Author
- Arlan
- Licence
- MIT
Loading…
A heavy white oblique word types itself, inflates into warped giant letters with coloured ink slipping out, swings over an arch and un-types.
Code
"use client";
import { CanvasStudy, easeInOut, prog, type Frame, type Painter } from "./stage";
export const BG = "#000000";
const DUR = 5.8;
const WORDS: [string, string][] = [
["grid", "#ff3d6e"],
["kern", "#2fd3ff"],
["serif", "#ffd23a"],
["glyph", "#7cff6b"],
["type", "#b07cff"],
];
const SLANT = -0.24;
export function create(): Painter {
return ({ ctx, w, h, t, fonts }: Frame) => {
ctx.fillStyle = BG;
ctx.fillRect(0, 0, w, h);
const n = Math.floor(t / DUR);
const u = t - n * DUR;
const [word, color] = WORDS[n % WORDS.length];
const letters = [...word];
const fs = Math.min(h * 0.2, w * 0.11);
ctx.font = `800 ${fs}px ${fonts.sans}`;
ctx.textBaseline = "middle";
const widths = letters.map((c) => ctx.measureText(c).width);
const total = widths.reduce((a, b) => a + b, 0);
// typing on and off
const typed = letters.filter((_, i) => u >= 0.15 + i * 0.1).length;
const untyped = letters.filter((_, i) => u >= 4.6 + (letters.length - 1 - i) * 0.08).length;
const visible = Math.max(0, typed - untyped);
if (visible === 0) return;
const grow = easeInOut(prog(u, 0.95, 0.75));
const shrink = easeInOut(prog(u, 3.25, 0.7));
const big = grow * (1 - shrink);
const maxS = (w * 0.86) / total;
const S = 1 + (maxS - 1) * big;
const swing = easeInOut(prog(u, 1.7, 1.5));
const arch = Math.sin(swing * Math.PI) * big; // bend of the arch
const sway = -Math.sin(swing * Math.PI * 2) * 0.7 * big; // over the arch and back
const slip = big; // coloured ink slipping out from under the white
const drawWord = (fill: string, ox: number, oy: number) => {
let x = -total / 2;
for (let i = 0; i < visible; i++) {
const cw = widths[i];
const xn = (x + cw / 2) / (total / 2); // −1..1 along the word
x += cw;
const warp = 1 + 0.32 * big * Math.sin(i * 1.7 + t * 2.4);
const lift = -arch * (1 - xn * xn) * h * 0.22 + sway * xn * h * 0.06;
const tilt = arch * xn * 0.5 + sway * 0.12;
ctx.save();
ctx.translate(w / 2 + (xn * total) / 2 * S + sway * w * 0.06 + ox, h / 2 + lift + oy);
ctx.rotate(tilt);
ctx.scale(S, S * warp);
ctx.transform(1, 0, SLANT, 1, 0, 0);
ctx.fillStyle = fill;
ctx.fillText(letters[i], -cw / 2, 0);
ctx.restore();
}
};
if (slip > 0.01) drawWord(color, fs * 0.07 * S * slip, fs * 0.05 * S * slip);
drawWord("#ffffff", 0, 0);
};
}
export default function InflateWord() {
return (
<CanvasStudy
create={create}
background={BG}
still={2.4}
label="A heavy white oblique word types itself, inflates into giant warped letters with coloured ink slipping out beneath, swings over an arch and un-types."
/>
);
}
Prompt
Build "Inflating word", a text animation. What it does: A heavy white oblique word types itself, inflates into warped giant letters with coloured ink slipping out, swings over an arch and un-types. Write it as a single self-contained React component in TypeScript that fills its parent (w-full h-full), styled with Tailwind CSS. Technique: an HTML canvas, a requestAnimationFrame loop, pointer tracking. It sits on a dark background, in a 16:10 frame. Respect prefers-reduced-motion with a still state, pause work when the element is off screen, and keep it accessible. Original: https://www.arlan.me/vault (Arlan, MIT).