Same page in. Same frames out.
A Vawe film is an HTML page that the renderer seeks to each frame's time. Ask for the frame at 4.2 seconds twice, in any order, and it comes back the same. That is not a tuning goal: the clock is replaced before the page runs, and a test renders pages twice and compares every frame.
The clock is fake, on purpose.
Before any script on the page runs, a virtual clock replaces every source of ambient state. Date and performance.now return film time, not the wall clock; requestAnimationFrame callbacks flush once per seek, and timers fire when the seek passes their due time. Math.random is reseeded from the seek time, so a scatter that looks random is the same scatter on every render.
The page itself is seeked, never played: the renderer sets the clock, calls window.seek(t) when the page defines one, then seeks every CSS and Web Animation to t before the screenshot. Ordinary code that reads the time keeps working, and still paints a pure function of t.
Date, performance.now film time in ms requestAnimationFrame flushed once per seek setTimeout/setInterval fire when t passes them Math.random reseeded from t
A test proves it, not a claim.
tests/media/render-page-determinism.test.mjs renders two fixture pages twice each and compares the decoded frame hashes. One page paints from window.seek(t) on a canvas; the other reads Date.now, performance.now and Math.random directly. Both renders must match frame for frame, and the frames must differ over time, so a page that froze would fail too. bin/vawe e2e runs it.
render page -> frame hashes A render page -> frame hashes B assert A === B assert frames differ over t
Parallel slices, the same pixels.
Because the frame at t depends on nothing before it, the renderer splits a film into fixed 60-frame slices and captures several at once, each on its own fresh page of one shared browser. The slice size is a constant, never the worker count: a page keeps raster state between seeks, so a frame reached by seeking can differ in the last decimal place from the same frame on a fresh page. Fixing the boundaries makes the pixels identical for any number of workers.
harness/media/render-page.mjs, SLICE_FRAMES60fps final, 30fps for a fast look.
One page becomes one mp4: bin/vawe ship renders 60fps at full size with motion blur and audio, and bin/vawe dev renders a half-size, 30fps, silent draft for a quick check. Frames are seeked at fractional times, so the render rate is independent of the page. Determinism is not a mode you opt into: every render goes through the same virtual clock.
When this property decides which engine to pick.
Byte-identical output matters most where nobody looks at every frame.