Reliability
A frame has to come out the same whichever frames the render showed before it.
This page has the rules that keep a seek the same on every run, the warning the development build prints when one is broken, and check(), which tests a page.
Read the time from the scene's local, time or clipFrame, and use random(seed) in place of Math.random().
Start no timers and use no CSS transitions, since both run on real time.
The development build, kinetic.wf.js, warns the first time it sees a rule broken, and check(), run in the browser console, seeks a sample of frames twice and compares them.
Keep every frame the same
A render can set the time to any frame in any order, and a renderer with parallel workers splits the frames among them, so a frame has to depend on its time alone. Six rules keep it so, and the development build warns the first time it sees one broken.
| Rule | Why | Warning |
|---|---|---|
Read the time from the scene: local, time or clipFrame. |
Date.now(), performance.now() and Math.random() differ on every call, so two renders of one frame differ.
random(seed) repeats its sequence for a seed, and noise(x, seed) repeats for the same x and seed. |
KN-140, KN-141 |
| Start no timers. | A timer, fetch() or requestAnimationFrame() started during a seek runs on real time, so the frame can be captured before or after it finishes. |
KN-142 |
| Use no CSS transitions. | A transition runs on real time, so a frame can be captured part-way through it.
Compute the value from local instead, as the tracks in keyframes do. |
KN-143 |
Format numbers with format(). |
toLocaleString() with no locale uses the machine's language, so a render machine can print different digits and separators from the preview.
format() uses en-US unless the options give a locale. |
KN-145 |
| Put an impact on a whole frame. | A render shows the times 0, 1/30, 2/30 and so on, so a contact between two of them shows on neither frame.
this.clipFrame counts whole frames from the clip's start, so this.clipFrame >= 19 lands the squash on frame 19 with no tolerance, as the showreel's opening ball does. |
KN-114, for a clip whose start or end falls between two frames |
The development build is kinetic.wf.js in place of kinetic.wf.min.js.
It watches each seek, warns once about each hazard, and for a call names the line of your code that made it.
Every code has an entry in the error code reference.
check()
check(), run in the browser console, seeks a shuffled sample of frames, the first and last included, then the same frames in reverse order, and compares what the page held each time: every element's text, inline style, class list and attributes, each CSS animation's position, and the pixels of each 2-D canvas.
It also counts the errors printed or thrown while it seeks, and reads the root's data-kinetic-ready.
const report = await wildflower.kinetic.check({ samples: 30 });
// { ok: true, frames: 810, samples: 30, ready: 'true', errors: 0 }
// { ok: false, reason: 'a seek threw', frames: 810, samples: 30, ready: 'true',
// errors: 60, firstError: "[WildflowerJS] Error in computed property 'bars': ..." }
// { ok: false, frame: 41, time: 1.3667, element: 'ol > li:nth-child(3)',
// property: 'style', first: 'width: 212px;', second: 'width: 230px;',
// ready: 'true', errors: 0 }
samples defaults to 30, ready is the root's readiness, and errors is the number of errors during the seeks.
A failing report says which part failed, and development builds print KN-160 as well.
reason: 'readiness failed':render()would reject with the same error before drawing a frame (through HyperFrames, the render waits at frame 0 until it times out), and the KN-13x error printed earlier says why.reason: 'a seek threw': the first error's message is infirstError.- Otherwise the first frame that differed, its time, a selector for the element, the property, and both values.
check() cannot see a binding that shows nothing or a list with no rows, because those look the same on every seek, so look at a few frames as well.
Run it once the page is ready: await wildflower.kinetic.ready() first, as in Readiness and check().
The check script
The skill's scripts/check.cjs runs the same check() in a headless browser, and is stricter: it fails on any KN line the development build prints, as well as on a failing report.
So a page can pass check() and still fail the script.
The line that does this most is KN-170, a single seek that took longer than 8 ms to settle.
On a busy machine, or one without a GPU, one slow seek can print it; a KN-170 that comes back on every run points at a scene doing too much work per frame.
In the reference
Readiness and the options of check() and render(): Readiness and check() and render().
The preview bar, the guard and the development diagnostics: The preview bar, the guard and diagnostics.
Many videos from one page: One page, many videos, and Rendering through HyperFrames.