A blue-and-white {5,4} tiling of right-angled pentagons in the Poincaré disk, glazed like a plate on a terracotta wall, turns about its center, then glides along a diameter as tiles swell through the middle and shrink toward the rim, ending with four pentagons meeting at the center.
Written and rendered by AI CSS Animations Oct 19, 2026
In hyperbolic geometry the angles of a triangle add up to less than 180 degrees, so regular polygons can meet in ways that are impossible on a flat plane, such as pentagons with five right angles, four at every corner. The Poincaré disk model, named for Henri Poincaré, maps the whole infinite plane into a circle: straight lines become arcs meeting the rim at right angles, and identical tiles look smaller and smaller toward the edge. M. C. Escher built his Circle Limit woodcuts of 1958 to 1960 on such tilings after seeing an illustration by the geometer H. S. M. Coxeter. Mathematicians, game designers and generative artists use the disk to show what infinite space can look like.
Make a 10-second film of a hyperbolic tiling in the Poincaré disk: a {5,4} tiling of identical right-angled pentagons, glazed like a blue-and-white plate, turns and glides under exact hyperbolic motions, tiles swelling at the center and shrinking without end toward the rim.
<inputs>
If I don't give you something, use the default and don't ask.
- Title: "Five right angles"
- Line: "A {5,4} tiling of the hyperbolic plane, seen in the Poincaré disk"
- Labels: "Every pentagon is the same size" · "Straight lines here are arcs that meet the rim at 90°" · "Four pentagons meet at every corner"
- Closing caption: "Angle sum 450°, where a flat pentagon needs 540°"
- Length: 10 seconds.
</inputs>
<direction>
- Palette, a glazed plate on a terracotta wall: wall brick #A8432E with a fine seeded plaster grain (3%); cobalt pentagons in two facet tones #22449A and #1A3474; porcelain pentagons in #F6F8FB and #DDE4EE; gold lines #D9A84A; the plate's rim a 4px gold ring with a soft shadow on the wall (#6E2A1C at 35%, 24px blur, 10px down); type cream #FBF1E4, secondary #F2C9B6. Blue and white against terracotta: cool plate, warm wall, gold drawing them together.
- The disk: radius 470px, centered at (1260, 540).
- The tiling, exact. For {5,4}, the pentagon's inradius is r_in = arccosh(cos(π/4) ÷ sin(π/5)) = 0.626870 and its circumradius R_c = arccosh(cot(π/5)·cot(π/4)) = 0.842482 (hyperbolic units). The central pentagon has an edge midpoint on the positive real axis at Euclidean radius m = tanh(r_in/2) = 0.303559; that edge is part of the circle centered (x_c, 0) = (1.798907, 0) with radius r_c = 1.495349, orthogonal to the rim (x_c² − r_c² = 1); the vertices at ±36° lie on it at Euclidean radius tanh(R_c/2) = 0.397975.
- Per pixel, in a fragment shader: for a disk point w (|w| < 1), u = e^{−iθ_C}·w; u = (u + a) ÷ (1 + a·u) with a = tanh(s/2); z = e^{−iθ_A}·u. Then fold z into the central pentagon, at most 64 times: rotate z by −k·72° with k = round(arg z ÷ 72°); if Im z < 0, reflect it (mirrors += 1); if |z − x_c| < r_c, invert z in that circle, z = x_c + r_c²·(z − x_c) ÷ |z − x_c|² (flips += 1), else stop. The pentagon's color is flips mod 2 (cobalt even, porcelain odd); its facet tone is (flips + mirrors) mod 2, so every pentagon shows ten alternating triangles like a pressed rosette.
- Lines of constant hyperbolic width: the hyperbolic distance from the folded z to the edge is d_e = asinh(| |z − x_c|² − r_c² | ÷ (r_c·(1 − |z|²))); gold where d_e < 0.035. A gold ring in every pentagon where the hyperbolic distance from its center, 2·atanh|z|, is within 0.02 of 0.345. Anti-alias with the hyperbolic size of one stage pixel at w, δ = 2·(1/470) ÷ (1 − |w|²), smoothstepping each edge over ±δ/2. Where δ passes 0.08, fade lines and rings out and blend the facet colors toward the average of all four tones by δ = 0.25, so the rim reads as fine texture instead of moiré. 4 samples per pixel.
- Motion, all exact disk automorphisms: θ_A turns 0 → 72° over 0.8–2.4 s on easeInOutCubic, a rotation about the central pentagon, which is a symmetry, so the tiling ends where it began; s runs 0 → 2.096221 (= 2·r_in + R_c) over 2.6–6.0 s on easeInOutSine, a hyperbolic translation along the horizontal diameter: tiles stream in from the right rim, swell through the center and shrink into the left rim; it carries the next pentagon's center (s = 1.253739) through the middle and ends with a corner where four pentagons meet at the exact center; θ_C turns 0 → 45° over 6.4–8.0 s on easeInOutCubic about that corner, ending with the four pentagons as a "+".
- Guides during the translation (fading in at 2.6 s, out by 6.2 s, 35% opacity): the axis as a 2px cream dashed line across the diameter; four flow lines, the arcs inside the disk of circles through the rim points (±1, 0) with centers (0, ±0.6) and (0, ±1.4), each with small cream arrowheads pointing left; gold ticks on the rim at (±1, 0).
- Labels, HTML, in Alegreya Sans SC 500, 22px, cream, each on a 1px cream leader ending in a 5px dot inside the disk, the text set on the wall left of the disk: label 1 on the central pentagon at 1.0–2.6 s; label 2 on a pentagon edge arc at 3.2–5.6 s; label 3 on the center corner at 6.6–8.4 s.
- Type: the title in Alegreya 500 italic, 84px, cream, at (120, 170); the line in Alegreya Sans 400, 26px, secondary, beneath, at most 560px wide; the closing caption in Alegreya 400 italic, 30px, cream, at (120, 960). Everything stays left of x 740.
- Easing: text rises 12px as it fades in over 0.6 s on cubic-bezier(0.22, 1, 0.36, 1).
- Premium: exact geometry in which every edge is a true geodesic arc meeting the rim at right angles, lines of constant hyperbolic width thinning toward the rim, a calm glide that shows tiles swelling and shrinking, a clean rim with no shimmer. Cheap: a fisheye filter on a flat tile pattern, unequal tiles, a rim full of moiré.
Banned: fisheye or radial warps of a flat pattern, tiles that aren't congruent, moiré or aliasing at the rim, glow, drop shadows on tiles, gradients across tiles, colors beyond the palette, anything flashing more than 3 times per second.
</direction>
<structure>
- 0.0–0.8s: the plate on the wall; the tiles appear ring by ring from the center (ring n = flips count fades in over 0.15 s from 0.06·min(n, 10) s), so the pattern grows out to the rim by 0.75 s; the title and line rise in.
- 0.8–2.4s: the central pentagon and everything with it turns 72°; label 1.
- 2.6–6.0s: the translation: the guides fade in and tiles stream from right to left, the next pentagon (porcelain, turned 36°) passing through the center at about 4.5 s; label 2.
- 6.0–6.4s: rest with a four-pentagon corner at the center. The 6.0 s frame: still, four pentagons meeting in an X, the disk sharp to the rim.
- 6.4–8.0s: the view turns 45° about that corner into a "+"; label 3.
- 8.0–8.6s: the closing caption rises in.
- 8.6–10.0s: hold, completely still.
For other lengths, stretch the translation; 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 defining window.seek.
- Engine: Canvas: one WebGL canvas (preserveDrawingBuffer: true, highp floats, drawn once per seek), its backing store sized from CSS size × devicePixelRatio, whose fragment shader evaluates the fold for every sample with θ_A, s and θ_C as uniforms; the wall, rim, guides and leaders on 2D canvases; type in HTML.
- θ_A, s and θ_C are closed-form functions of t; nothing is stored between frames.
</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 at 4× zoom that every edge is a smooth gold arc meeting the rim at right angles, lines thin toward the rim without breaking up, the rim shows no moiré or shimmer between frames, the frame at 2.4 s matches the frame at 0.8 s apart from labels, the 6.0 s frame has four pentagons meeting exactly at the center, and type and leaders stay clear of each other. 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.
A jazz-tempo film title sequence in flat vermilion, mustard, bone and black: hand-cut paper shards jab in on the beat,…
A Swiss-style event poster that builds itself on a visible 12-column grid: grotesque type rises out of baseline masks,…
A primary-colored circle, square and triangle roll, drop and turn into place on a black bar, assembling a constructed B…
A Constructivist poster in motion: red bars, a wedge piercing a black circle, halftone fields and condensed uppercase t…
More like this on the motion design inspiration board: Motion graphics inspirationLine art animation inspiration