Under the hood

Same seed, same video, frame for frame: deterministic rendering

Why MotionMaker bans Math.random at render time, how one seed drives every variation, and how a shared beat grid locks the edit to the sound.

· 5 min read

A generated film you cannot reproduce is a film you cannot fix. If a flaw shows up on frame 40, you need to run the exact same video again, not a close relative. This article explains how MotionMaker makes its films deterministic: one seed gives back the same video, frame for frame. It also covers the other side of it, which is how two separate generations still come out different.

The problem: randomness is the enemy of debugging

An animated video is computed one frame at a time. At 30 frames per second, a 17-second film has 516 of them. Each frame has to agree with the one before: a particle that jitters, a layout variation drawn “at random” every time it is computed, and the film changes from one render to the next. Worse, rendering runs several processes side by side (two, on our server), so frames are not necessarily computed in the order you watch them.

If a frame depends on what happened before it, or on a draw specific to that run, parallel rendering produces slightly different films on different days. You can no longer compare two versions, or know whether a fix really fixed anything. Hence the project’s first rule.

The seed: one grain per generation, not a die rolled on every frame

The seed is an integer computed once, when the generation starts. It is derived from the brand name and the moment of generation, run through a simple, stable string hash with no external dependency. So it is not a draw in the Math.random sense: once computed, it is fixed and travels with the film.

There are two consequences. First, two generations (two brands, or the same brand run again) almost always get different seeds, hence different compositional choices: yesterday’s film does not look like today’s. Second, given one seed, everything can be replayed. Chance is spent once, upstream; the renderer decides nothing.

Mulberry32: a tiny, fast generator with no surprises

To turn that seed into a stream of numbers, MotionMaker uses mulberry32, a small pseudo-random generator that fits in a few lines. It is fast and repeatable: same seed, same sequence. That is enough for staging decisions. It makes no cryptographic claim and does not need to, since all it picks is a transition variant, not a password.

So that each scene draws its own sequence without stepping on the others, the generator is derived from the seed combined with a “salt”: a string naming the purpose (the tempo, a given scene, a given aspect). Changing the order in which draws are consulted moves nothing for the neighbors, and adding a new draw inside one scene does not disturb the rest.

One place where decisions are made

Choosing a visual treatment for a scene goes through a single function: it keeps the option the user explicitly asked for if there is one, and otherwise the option the seed points to. Scenes no longer draw their own lots. A single decision point has a very practical advantage: when a variant surprises you, you know where to look, and you can force it to compare it with another.

The same goes for what we call the motion signature, the movement character that runs through the whole film, transition length included. Rendering and the pipeline read the same function, and the overlap constant is never duplicated. Two copies of one number always end up disagreeing; a single source cannot. For the craft behind those overlaps, see anticipation and overlap in motion design.

The beat grid: edit and sound start from the same tempo

Determinism is not only for reproducing, it is also for synchronizing. The edit’s tempo is drawn from the seed within 96 to 124 beats per minute: below that it drags, above it shots become too short for a line of text to be readable in a 15 to 25 second format. Cuts land on beats, which gives the edit the breathing you see in professional references.

And that same grid is then used to build the soundtrack. Edit and sound are computed from one tempo, so they cannot drift apart: synchronization is exact by construction, never tuned by ear.

Two traps that rounding shot by shot does not fix

In practice two measured defects had to be corrected. First, every transition consumes some overlap, so a cut falls at “duration minus overlap”, not at “duration”. On a real edit at 105 beats per minute, cuts landed on frame 72 where the beats fall at 68.6 and 85.7: off the grid, hence out of step with the sound.

Second, a beat is rarely a whole number of frames (17.14 at 105 BPM). Round shot by shot and the error accumulates: 1.4 frames of drift at the last cut of a seven-shot edit, and more as the film gets longer. The fix is to compute cut positions in absolute terms on the grid (cumulative beats from the start) and derive the durations from them. The error then stays within half a frame at every cut and never adds up.

Making a model-written film deterministic

The “Carte blanche” mode raises a harder problem: the film’s code is written by a model, and free-form code is full of sources of nondeterminism (CSS animations, timers, the clock, randomness). The film therefore has to expose a pure render function that receives a frame number and draws that frame, nothing else. CSS animations, browser animation callbacks, timers and the date are frozen, and Math.random is reset at every frame.

And we do not just promise it: the check recomputes one frame after the others to confirm it comes out the same. The comparison is perceptual (more than 1% of clearly different pixels), because comparing exact fingerprints raised false alarms on letter antialiasing. That lesson is typical: determinism has to be measured with the right tolerance, it cannot simply be declared.

What it changes for you

  • Targeted fixes: a flaw can be reproduced, so it can be corrected and checked on the same video.
  • Different films from one generation to the next: the seed varies, so the composition does too.
  • Sound that lands on the cuts, because the tempo is shared.
  • Honest comparisons: two versions of the engine are judged on the same seed, not on two lucky draws.

A last reminder: “check by rendering, not by rereading”. Several hypotheses were disproved by the render itself, and determinism is what makes those comparisons possible. Next, read measuring instead of assuming: render cost and iPhone memory. For the overview of the tool: how a website becomes a video.

Frequently asked questions

Does one seed really give back the same video?
That is the project rule: every variation goes through the seed or the current frame, never through Math.random at render time. With an identical seed the film must be identical frame by frame.
Why do two films for the same brand look different?
The seed is derived from the brand name and the moment of generation. Running again almost always yields a different seed, hence different compositional choices.
How does the sound stay locked to the cuts?
The edit and the soundtrack are computed from the same tempo. Cut positions are computed in absolute terms on the grid, which keeps the error within half a frame.
Does determinism also apply to Carte blanche mode?
Yes: a pure render function, frozen clocks, and a check that recomputes one frame to confirm it comes out the same.

Paste your website address. The film follows.

Paste your address: your site is read, then your first film is made in your account. Free credits at sign-up, watermark-free MP4 films.

Nothing is made up: figures, reviews and names come from your site. The app interface is currently in French.