Skip to content

Title sequence

“cmotion” assembled from one extrude(text.glyph(...)) per letter, so each turns on its own y axis. The spins are staggered and settle by about four seconds; then the word sits quiet while two coloured lights sweep the hue wheel and wobble their direction. A 2D background composes behind the 3D scene.

Because the letters are placed by hand with translate(x:), the scene sets camera(distance: 2166) so the native renderer’s 3D view is exactly 1080 world-px tall — the same pixel-honest mapping the web viewer uses — and the per-letter advances line up in both. Every motion is tuned to loop seamlessly over the 12-second duration.

rendering…
runner "0.0.1";

use std.shapes.*;
use std.mesh3d.*;
use std.text;
use std.lighting.*;
use std.scene3d.*;
use std.anim.*;

scene title(duration: Duration = 12s) -> Frame {
  let bg = rect(width: 1920px, height: 1080px, fill: oklch(0.97, 0.012, 95));

  // Per-letter Y spin, staggered by 0.3s, each 360° over 2s then held.
  let r0 = animate { 0s => 0deg, 2.0s => 360deg, 12s => 360deg } with { easing: easing.in_out_cubic };
  let r1 = animate { 0s => 0deg, 0.3s => 0deg, 2.3s => 360deg, 12s => 360deg } with { easing: easing.in_out_cubic };
  let r2 = animate { 0s => 0deg, 0.6s => 0deg, 2.6s => 360deg, 12s => 360deg } with { easing: easing.in_out_cubic };
  let r3 = animate { 0s => 0deg, 0.9s => 0deg, 2.9s => 360deg, 12s => 360deg } with { easing: easing.in_out_cubic };
  let r5 = animate { 0s => 0deg, 1.5s => 0deg, 3.5s => 360deg, 12s => 360deg } with { easing: easing.in_out_cubic };
  let r6 = animate { 0s => 0deg, 1.8s => 0deg, 3.8s => 360deg, 12s => 360deg } with { easing: easing.in_out_cubic };

  // The "i" hops up and down instead of spinning, and rests slightly raised.
  let ijump = animate {
                0s   =>  30px,
                1.3s => 250px,
                2.1s =>  30px,
                2.8s => 165px,
                3.5s =>  30px,
                12s  =>  30px,
              } with { easing: easing.in_out_cubic };

  // Bright neutral lighting so each letter shows its own colour; a slow
  // wobble on the key keeps the highlights alive.
  let key  = directional(from: vec3(wave(amplitude: 3, period: 8s), 4, 6), intensity: 1.7);
  let fill = directional(from: vec3(-4, -2, 4), intensity: 0.8);
  // A subtle spotlight pool drifts across the word (most visible at rest).
  let sweepx = animate { 0s => -1100px, 6s => 1100px, 12s => -1100px } with { easing: easing.in_out_cubic };
  let spot   = spotlight(at: vec3(sweepx, 120px, 360px), intensity: 4.0, range: 820px, color: oklch(0.98, 0.01, 95));
  let lights = [ ambient(0.40), key, fill, spot ];

  // Each letter a different bright colour, scattered (not a smooth ramp).
  let c0 = extrude(text.glyph("c", size: 430px), depth: 43px).material(fill: oklch(0.70, 0.20,  25), metalness: 0.0, roughness: 0.45).rotate(y: r0).translate(x: -567px);
  let c1 = extrude(text.glyph("m", size: 430px), depth: 43px).material(fill: oklch(0.83, 0.16,  92), metalness: 0.0, roughness: 0.45).rotate(y: r1).translate(x: -313px);
  let c2 = extrude(text.glyph("o", size: 430px), depth: 43px).material(fill: oklch(0.70, 0.19, 150), metalness: 0.0, roughness: 0.45).rotate(y: r2).translate(x: -59px);
  let c3 = extrude(text.glyph("t", size: 430px), depth: 43px).material(fill: oklch(0.66, 0.16, 245), metalness: 0.0, roughness: 0.45).rotate(y: r3).translate(x: 108px);
  let c4 = extrude(text.glyph("i", size: 430px), depth: 43px).material(fill: oklch(0.70, 0.23, 350), metalness: 0.0, roughness: 0.45).translate(x: 225px, y: ijump);
  let c5 = extrude(text.glyph("o", size: 430px), depth: 43px).material(fill: oklch(0.74, 0.20,  55), metalness: 0.0, roughness: 0.45).rotate(y: r5).translate(x: 368px);
  let c6 = extrude(text.glyph("n", size: 430px), depth: 43px).material(fill: oklch(0.72, 0.17, 195), metalness: 0.0, roughness: 0.45).rotate(y: r6).translate(x: 570px);

  compose [
    bg,
    render3d(compose [c0, c1, c2, c3, c4, c5, c6], lights: lights, camera: camera(distance: 2166)),
  ]
}