Motion as code with an agent|INEMA.CLUB-PRO

Lesson 1 · 15 min

Video is code: a composition with time and deterministic render

In motion graphics as code, the video is a page that knows how to draw any instant. A headless browser asks for frame 0, then 1, then 2, and FFmpeg stitches them into an MP4. If the same input always gives the same frames, you can redo, fix, and version the video like code. In this lesson you prove it on your own machine.

Start the lesson
A film strip on a cold light table, with a white circle that grows from frame to frame.

In this lesson you will

  1. 1The four pieces: composition, clock, headless browser, and encoder
  2. 2Compare two renders frame by frame with one command
  3. 3Why Math.random and Date.now break the video
  4. 4Seeded random: variety without losing repeatability

Goal: prove on your own machine that the same composition gives the same frames, and find what breaks that.

01From code to MP4

Tap each piece. HyperFrames (HTML) and Remotion (React) follow the same design.

Across different machines, GPU and fonts can change a few pixels; to repeat it on another machine, the 0.8.176 render has --docker ("deterministic render") and --no-browser-gpu.

Deterministic = run it twice in the same environment and get the same frames. That's what lets you fix one detail without redoing the rest.

02What breaks repeatability

Three sources of chance that the HyperFrames lint looks for.

1. Random draw

Each render draws again: the same 12 points show up in different places.

The fix is a seeded draw (mulberry32): the same seed gives the same numbers, every time.

Fixconst sorteio = mulberry32(42); then sorteio() in place of Math.random().

03The test run: two renders, compared frame by frame

Two nearly identical compositions: one uses Math.random, the other a seeded draw. Each one is rendered twice and compared by the hash of each frame.

Git · clone the course (once)
git clone https://github.com/inematds/formacao-cinema-ia-motion-v72 ~/projetos/formacao-cinema-ia-motion-v72
Terminal · two renders of each and the comparison
K=~/projetos/formacao-cinema-ia-motion-v72/kit
export HYPERFRAMES_NO_TELEMETRY=1
E=${TMPDIR:-/tmp}/ensaio-motion/a1; rm -rf "$E"; mkdir -p "$E"
for ex in a1-sorteio a1-semente; do
  cp -r "$K/exemplos/$ex" "$E/$ex"; cd "$E/$ex"
  for n in 1 2; do
    npx -y hyperframes@0.8.176 render --quality draft --workers 1 --quiet --output "render-$n.mp4" > /dev/null 2>&1
  done
  echo "## $ex"; bash "$K/comparar-quadros.sh" render-1.mp4 render-2.mp4; echo "saída: $?"
done
Example from the repository
## a1-sorteio
DIFEREM: 89 de 90 quadros
saída: 1
## a1-semente
IGUAIS: 90/90 quadros
saída: 0

That's 90 frames (3 s at 30 fps). With Math.random only the first one matches; with the seed, all 90 do. The first render downloads hyperframes@0.8.176 and the browser, so it takes longer.

04The lint finds the chance before the render

The same pair of compositions run through the HyperFrames lint. It points to the line and suggests the seed.

Terminal · lint of both compositions
K=~/projetos/formacao-cinema-ia-motion-v72/kit
export HYPERFRAMES_NO_TELEMETRY=1 NO_COLOR=1
for ex in a1-sorteio a1-semente; do
  cd "$K/exemplos/$ex"
  npx -y hyperframes@0.8.176 lint; echo "saída: $?"
done
Example from the repository
◆  Linting a1-sorteio/index.html

  ✗ [~/projetos/formacao-cinema-ia-motion-v72/kit/exemplos/a1-sorteio/index.html:24:24] non_deterministic_code: Script contains `Math.random()` which produces non-deterministic output. Renders may differ between frames or runs.
    Fix: Use a seeded PRNG (e.g. a simple mulberry32) so renders are deterministic across frames.

◇  1 error(s), 0 warning(s)
saída: 1
◆  Linting a1-semente/index.html

◇  0 errors, 0 warnings
saída: 0

The lint exits with code 1 when it finds an error: you can use it as a gate before any render.

05Practice

Test run, lint, and your seed.

E1 · See the chance and the seed

10 min

Clone the course into ~/projetos and run the "two renders of each and the comparison" block.

Done when: a1-sorteio ends with DIFEREM: 89 de 90 quadros and saída: 1; a1-semente with IGUAIS: 90/90 quadros and saída: 0. (On your computer the number of differing frames may vary; what can't happen is the seed giving a different result.)

E2 · The lint as a gate

5 min

Run the lint block.

Done when: a1-sorteio shows non_deterministic_code and saída: 1; a1-semente shows 0 errors, 0 warnings and saída: 0.

E3 · Another seed, same guarantee

10 min

In /tmp/ensaio-motion/a1/a1-semente, replace mulberry32(42) with mulberry32(7), render twice (render-3.mp4 and render-4.mp4, using the same command from the block), and compare: 3 with 4, then 1 with 3.

Done when: bash ~/projetos/formacao-cinema-ia-motion-v72/kit/comparar-quadros.sh render-3.mp4 render-4.mp4 → IGUAIS: 90/90 quadros; the same with render-1.mp4 render-3.mp4 → starts with DIFEREM:.

06Lesson quiz

The explanation appears after you answer.

Question 1 of 4

Who decides which instant the composition draws?

07In three sentences

Composition + clockThe video is a page that draws any instant the engine asks for.
DeterminismSame input, same frames: that's what lets you fix one detail without redoing the rest.
Controlled randomnessSeeded draw, engine time, and data saved in the folder; the lint keeps watch.

Going further

Next lesson