AI CSS Animations

Type on Ribbons

Sentences ride long printed paper ribbons that twist and loop through a deep green space, bone, saffron and terracotta strips softly shading each other, until one ribbon straightens and turns square to the camera so a single line reads, centered and still.

Written and rendered by AI CSS Animations Oct 18, 2026

Type
prompt
Made with
Claude Opus 5.5 in Claude Code
Built in
three.js
Topics
Kinetic type · 3D
What Claude made from the prompt below in Claude Code: one HTML file rendered frame by frame to video. Silent preview.

About this style

Setting words along a path is an old lettering device, found on seals, coins and the painted ribbons of signs and banners. Motion designers carry it into 3D by printing a line of type onto a ribbon that bends and twists through space, so the eye follows the words as the ribbon travels. Done well, the ribbon keeps its face to the viewer wherever the words matter and twists only in the gaps between them. Brand films, conference openers and music videos use it to make a sentence feel like a journey.

The prompt

Make a 10-second 3D kinetic type piece: sentences ride long printed paper ribbons that twist and loop through space, then one ribbon straightens into a single readable line.

<inputs>
If I don't give you something, use the default and don't ask.
- Event: Larchmere Writers' Festival (fictional), 3–6 April.
- Ribbon 1: "Every story starts as a line"
- Ribbon 2: "and wanders off the page"
- Ribbon 3, small caps, repeating: "LARCHMERE WRITERS' FESTIVAL · 3–6 APRIL"
- Final line: "Follow the line."
- Length: 10 seconds.
</inputs>

<direction>
- Palette: background a deep green field, #1F5C4C at the center falling to #0F3029 at the corners (a large plane far behind the ribbons, never black); ribbon 1 bone #F3ECDD with ink #1B1A17 type and a terracotta #D1623F back; ribbon 2 saffron #F2B441 with ink type and a bone back; ribbon 3 terracotta with bone type and a deep green #154238 back; each ribbon's edges a shade lighter than its faces; HTML type #F3ECDD.
- Ribbons are printed paper strips with real thickness. Each is a strip mesh along a CatmullRomCurve3: a front face, a back face 0.012 units behind it and two thin edge strips, about 6 segments per unit of length and 8 across. Wide ribbons are 0.9 units across, ribbon 3 is 0.5. Orient the strip with parallel-transport frames (Frenet frames flip), then add a twist angle φ(s, t) along it. Front and back are separate MeshStandardMaterials (roughness 0.8, a faint generated paper-fiber bump map); the front carries the print.
- Print: render each ribbon's print once at load onto a long canvas (8192 × 512 for the wide ribbons) in "Fraunces" 600 at opsz 72, cap height 46% of the ribbon's width, ink color, with 1px hairlines 8% in from each long edge like printed tape; ribbon 3 in "DM Sans" 600, uppercase, tracked 0.18em. Ribbon 2 repeats its sentence with a 2em gap and a small terracotta diamond between repeats; ribbon 3 repeats endlessly; ribbon 1 prints its sentence twice with diamonds, then a 3-unit gap, then "Follow the line.", then blank paper. Use sRGB color space, mipmaps and maximum anisotropy so the type stays sharp at glancing angles. Words never read mirrored: on the front face they run left to right in the direction the print travels.
- Flow: the ribbons hold their shapes (control points drift on slow sines, ±0.3 units, 7–11 s periods) while the print feeds along them at 0.5 units/s, like tape pulled through space. Each wide ribbon has a reading stretch about 9 units long, nearly flat, with zero twist and its front facing the camera, long enough to show its whole sentence; half-twists (180°) and one full loop happen outside the reading stretches, in the gaps between repeats, never through a word. Solve each feed's phase so ribbon 1's sentence is centered in its stretch at 3.0 s and ribbon 2's at 5.6 s; both read whole on the 6 s frame.
- Layout: camera fov 35 at z = 12 (1 unit is about 143px at z = 0). Ribbon 1 rises from far lower left (z −10) with a half-twist, crosses the upper middle in its reading stretch (centered on screen y 400, from about x 260 to 1660), then twists and dives out past the lower right near the camera. Ribbon 2 enters from far upper right, turns one full loop at the right, crosses the lower middle in its reading stretch (centered on y 720), and leaves left with a half-twist. Ribbon 3 weaves behind both at about z −4 with two half-twists. They cross in depth but never touch: keep 0.3 units of clearance.
- Light, bright from the first frame: a HemisphereLight (sky #FFF4E2, ground #0F3029, 0.9), a warm key DirectionalLight #FFE9CC at 2.4 from the upper left front with soft shadows (PCFSoftShadowMap, 2048 map, small radius) so ribbons shade each other, a cool fill #BFE6DA at 0.6 from the right, and a rim light from behind that catches the paper edges. ACESFilmic tone mapping, exposure 1.1, sRGB output. A face turned away from the key still reads clearly: no ribbon face falls below about 35% brightness.
- The ending: ribbons 2 and 3 sweep out of frame (2 down-left, 3 up and back, 1.0 s, easeInCubic). Ribbon 1's control points ease onto a straight horizontal line at screen y 520 across the whole frame, its twist easing to 0 and its front square to the camera (easeInOutCubic, 1.4 s), while its feed speeds up and settles (a C1-smooth position curve, solved analytically) so "Follow the line." stops centered at x 960. It reads at about 96px. Beneath it, in HTML, "Larchmere Writers' Festival · 3–6 April" in DM Sans 500, 28px, tracked 0.12em, centered on baseline y 700.
- Motion is continuous and calm: no cuts, no bounce, nothing faster than the final feed. A slow camera push from z 12 to 10.5 with a yaw drift that rises to 6° and returns to 0° by 8.2 s, all easeInOutSine.
- Premium: paper with thickness and a printed back, words that read wherever the eye lands, twists placed between words, soft ribbon-on-ribbon shadows. Cheap: flat text on a sine path, mirrored words, a glowing ribbon in black space.
Banned: mirrored or upside-down words in a reading stretch, a twist through a word, glow or bloom, neon, black backgrounds, chrome, ribbons passing through each other, 3D text geometry, motion blur, camera shake, anything flashing more than 3 times per second.
</direction>

<structure>
- 0.0–1.2s: the green field, softly lit; ribbon 1 sweeps in as its control points ease from off screen lower left into its shape (easeOutCubic), its print already feeding; ribbon 3 drifts in behind.
- 1.2–3.4s: ribbon 1 settles; "Every story starts as a line" slides into its reading stretch, centered at 3.0 s; the camera push begins.
- 2.6–4.4s: ribbon 2 swings in from the upper right, its loop turning over once as it arrives, saffron front and bone back alternating through the twist.
- 4.4–6.6s: all three feed; "and wanders off the page" is centered in ribbon 2's stretch at 5.6 s; the two sentences read together, one above the other, through 6.6 s.
- 6.6–8.2s: ribbons 2 and 3 sweep away; ribbon 1 straightens and squares to the camera while "Follow the line." feeds in and stops centered at 8.2 s.
- 8.2–8.8s: the festival line rises 12px as it fades in.
- 8.8–10.0s: hold, perfectly still.
For other lengths, stretch the 4.4–6.6s beat; keep at least 1 s of hold.
</structure>

<build>
- One self-contained index.html (inline CSS and JS), a 1920×1080 (16:9) stage scaled to fit the window, designed for 60fps. No image, video, texture or model files: every shape and texture is generated in code.
- window.seek(t), t in seconds, computes every visual from t alone: no CSS transitions or CSS animations, no setTimeout/setInterval, no state carried between frames, randomness only from a seeded PRNG. Anything cumulative is solved analytically from t or precomputed once at load at a fixed step. Past the end, seek clamps to the last frame.
- Without ?render in the URL, drive seek from requestAnimationFrame so it plays in a browser (loop after a 1s end hold); with ?render, don't start that loop.
- Google Fonts via <link> are fine; await document.fonts.load() for each face before drawing the print canvases and defining window.seek.
- Engine: three.js as an ES module pinned to three@0.160.0 through an import map on jsDelivr (`three` and `three/addons/`). WebGLRenderer with antialias, preserveDrawingBuffer: true and setPixelRatio(window.devicePixelRatio); render once at the end of every seek. The festival line is HTML over the canvas.
- Each seek rebuilds the ribbon vertex positions from the control points at t and sets each print's texture offset from its feed position at t; nothing is stepped frame to frame.
</build>

<check>
Before showing me, step through one frame per second (t = 0…10): screenshot each and look at it. Fix overlaps, clipped or cramped text, and anything off-style or Banned. Check every word in a reading stretch reads left to right and unmirrored, no twist passes through a word, ribbons never intersect, every face is clearly lit, the 6 s frame shows both sentences whole, and "Follow the line." sits centered and square to the camera in the final frame. Confirm seek(10) and seek(11) match, so the final frame holds.
</check>

Ours, and free to use under CC0: copy it, change it, ship it. No credit needed.

Similar prompts

More like this on the motion design inspiration board: Kinetic typography inspiration3D motion design inspiration