Skip to content

Latest commit

 

History

History
112 lines (86 loc) · 4.12 KB

MOTION_SPEC.md

File metadata and controls

112 lines (86 loc) · 4.12 KB

Motion and layout specification

Everything below lives in src/scene/motion.ts unless stated otherwise. The web runtime and the video compositions both read these tables, which is what keeps a rendered frame and a live frame identical.

Coordinate system

Quantity Value
Logical scene 1240 × 1240 units
Card size 704 × 352 units
Front card centre (620.5, 620)
Web projection Container-query units against the scene box
Video projection Absolute pixels, 2100 × 1050 at a 3000 × 3000 scene

The web stylesheet expresses the card in cqw, so the deck scales with its container and stays sharp at any size. src/remotion/CardFace.tsx resolves the same layout to pixels through CARD_UNIT = 2100 / 704.

Slots, not cards

The choreography has exactly four motion slots. A deck of four or more cards maps onto them: slot = card index modulo 4. Each slot owns a distinct arc, so a seven-card loop never repeats the same movement twice in a row.

Slot Exits Peak rotation Transition Z-swap at
0 right 8.20° 22 frames 45.5%
1 left −11.00° 22 frames 45.5%
2 right 19.50° 22 frames 50.0%
3 left −11.24° 23 frames 59.0%

Timing is authored in whole frames at 59.94 fps (FRAME_MS). Autoplay waits 8 frames, then advances every 52 / 50 / 52 / 51 frames by slot.

Anatomy of one transition

  1. Outward — the front card travels off its exit side, shrinking to ~48% and rotating toward its peak angle. Sampled from per-slot tables, not from a generic easing curve.
  2. Z-swap — at the slot's swap point the outgoing card drops behind the stack. The incoming card is already most of the way to the front.
  3. Return — the outgoing card curves back into the fan and settles into its resting pose.
  4. Promotion — the incoming card lingers in its fanned pose, then catches up quickly as the outgoing card crosses behind it (INCOMING_WEIGHT_SAMPLES).

EFFECTIVE_LOCAL_FRAMES remaps raw progress so the deliberate whole-scene holds land on the same frames every time. The map stays piecewise linear between frames, so live playback holds and catches up without quantising all motion to frame boundaries.

Material

The glass is animated, not static. materialProgress drives a five-stop gradient from the back-card alphas [0.18, 0.27, 0.31, 0.23, 0.19] to the front-card alphas [0.44, 0.43, 0.40, 0.28, 0.22], together with the backdrop blur (1.45 → 3.39 cqw) and saturation (0.84 → 0.78).

Two rules hold everywhere:

  • Only the surface layer is blurred. The content layer — the prompt copy — is never filtered, so text stays native and sharp through the motion.
  • Blur is a depth and velocity accent confined to the stack crossover. A card is still crisp during its first outward frames.

Deterministic QA

The web runtime accepts query parameters and exposes a debug handle:

/?card=3                 render a stable card
/?card=3&progress=0.45   freeze a transition mid-flight
/?autoplay=1             run the loop unattended
window.__cardStack.advance()
window.__cardStack.renderCard(2)
window.__cardStack.renderProgress(0.5)
window.__cardStack.topCard()
window.__cardStack.ready

data-ready, data-animating and data-top-card on the stack element make the same state visible to a test runner without reading internals.

Video timeline

src/remotion/timeline.ts holds the cut: 780 frames at 60 fps (13.00 s) with six swipes.

Swipe Starts at frame Duration Slot
1 93 22 0
2 208 22 1
3 323 22 2
4 437 23 3
5 552 22 0
6 666 22 1

Each swipe carries the matching stereo cue: the right-exit cue for slots 0 and 2, the left-exit cue for slots 1 and 3.

Sound

src/scene/nativeAudio.ts synthesizes the cue at runtime from filtered noise and two oscillators — no audio file is loaded in the browser. scripts/generate-sfx.mjs is a sample-accurate offline port of that same graph, so the WAV files the video renders with are the cue you hear on the page.