Browser Animation Capture: Separate Frame Time from Render Time

Plan repeatable browser-game animation capture with explicit frame times, a reproducible clock lab, and checks for canvas capture and asynchronous rendering.

colinkoko6 min read

Key takeaways

  • An output frame rate is not a real-time rendering benchmark.
  • Document whether frame zero is the initial state or follows a simulation step.
  • A chosen clock does not make asynchronous scene state deterministic.

Choose animation time before you capture a frame

For a repeatable browser-game trailer, choose each frame's animation time explicitly, finish drawing that state, then capture it. A 60 fps output file does not prove the scene ran at 60 fps. Keep the animation timeline, the time spent rendering, and the encoded playback timeline separate.

Three film frames show a copper-and-teal mechanical bird in successive wing poses above glowing timeline marks.
Concept illustration of sampled motion. Not a browser screenshot, rendered game asset or capture benchmark.

On October 13, 2026, Xiao described an ASCII-world walkthrough whose capture advances a virtual clock rather than recording an ordinary live screen. The linked Crewship repository exposes the capture script. That makes it a useful case study in timing boundaries, not evidence that the same method works unchanged for every game.[1][2]

Three clocks answer different questions

ClockQuestionWhat to record
Scene timeWhich pose or simulation state is this?Chosen timestamp and simulation step
Wall timeHow long did producing this frame take?Actual render/capture duration
Playback timeWhen should a viewer see it?Output frame index and presentation timestamp
A planning model for an offline animation export.

For a turntable driven directly by time, frame i can sample t = i / fps. A stateful physics or camera controller is different: it may need every intervening update, not a jump to the requested timestamp. A frame schedule is therefore a contract for your exporter, not a replacement for the scene's update rules.

During interactive playback, requestAnimationFrame normally follows the display refresh rate and is often paused in background tabs. MDN warns against advancing animation by a fixed amount per callback: high-refresh screens would move it faster. Use elapsed time for ordinary playback, while designing offline export as a separate path.[5]

What the inspected capture script actually controls

At revision 8b8b842, the capture script replaces the page's requestAnimationFrame queue and performance.now before navigation. Its step function delivers queued callbacks a chosen time, then the outer loop requests a PNG screenshot. We inspected that source; we did not run the upstream capture or reproduce the creator's video.[2]

Two details are easy to miss. First, output frame count and page time are not identical: the loop applies an optional clock multiplier. Second, the page time advances before the screenshot, so its first saved frame is not the same convention as the zero-time first frame in our companion. Record the convention instead of silently comparing mismatched frame numbers.[2]

The workflow also requires site-specific adaptation and deterministic initialization, and says to compare segment logs. It is not a generic URL-to-perfect-video command. Its documented lapse option intentionally accelerates the page clock on some route sections.[3]

Reproduce a small frame-clock lab

The downloadable companion is an original Python standard-library exercise. It computes a two-second, 60 fps schedule using exact rational arithmetic and compares authored callback schedules. No browser, game engine, model, video encoder or upstream code runs in this experiment.

The 120 frame samples use indices 0 through 119. Their timestamps run from 0 to 119/60 seconds, approximately 1.9833 seconds. If each frame is displayed for 1/60 second, the sequence occupies two seconds. Adding a separate sample at exactly 2 seconds would produce 121 frames under this convention.

Use the generated CSV to inspect the indices and the JSON report to compare motion calculated from elapsed time with a deliberately naive per-callback increment. This isolates a timing mistake you can fix before involving an expensive render. The authored schedules are examples, not measurements of dropped frames on a real device.

  • Run the companion tests before changing its inputs.
  • Choose whether frame zero means the initial state or the state after one simulation step.
  • Keep frame indices contiguous when splitting a capture into segments.
  • Compare identical scene states at matching times, not merely files with matching names.

At a separate two-second endpoint, elapsed-time motion reaches 240 units in all three authored schedules. The naive rule reaches only 120 units at 30 updates per second, versus 240 at 60. At the last captured timestamp, even the elapsed-time callback state can be stale: the 30 Hz held value is 236 while the directly evaluated value is 238. Correct speed and current sampled state are separate checks.

A capture request does not assign the scene a time

Canvas captureStream creates a real-time media stream. With a rate of zero, automatic frame capture is disabled and the returned track can request frames manually. This controls when canvas pixels enter the stream; it does not itself set your animation clock or declare the intended duration of each exported frame.[6][7]

Check compatibility before choosing that path: MDN marks requestFrame as having limited browser availability. Also keep the canvas origin-clean. captureStream can fail with SecurityError when the canvas includes content that violates that requirement. Fix asset serving and permissions rather than treating a capture failure as a reason to bypass browser restrictions.[6][7]

Make the rest of the scene repeatable

For your own exporter, write down the following preconditions and verify them in the actual application. They are proposed checks, not properties we tested in Crewship or Goblin3D.

  • Load required models, textures and fonts before beginning the frame schedule; surface load failures.
  • Reset camera, animation state and random seeds for each run.
  • Audit timers, workers, network responses and other clocks that a page-level animation override may not control.
  • Wait for the relevant asynchronous drawing work before saving pixels; advancing a callback alone may not finish it.
  • Fix viewport and pixel density, then inspect crops, transparency and color in the intended player.
  • Check the encoded file's frame count, presentation timing and audio alignment separately from the image sequence.

For a long stateful scene, parallel capture needs a valid starting state for every segment. The inspected workflow replays from frame zero and only saves its assigned half-open range. That can avoid missing history, but it repeats computation; compare logs before joining segments. It does not establish cross-machine pixel identity.[2][3]

Keep the asset and capture handoffs separate

The inspected Crewship revision uses FSL-1.1-ALv2, with restrictions during its initial period and a later Apache-2.0 transition under the license terms. Do not assume the public repository grants immediate unrestricted reuse. This companion copies none of its code, artwork or video.[4]

Goblin3D can create 3D assets from images or text, with GLB, OBJ and FBX exports on paid plans. If you bring an exported prop into your own browser scene, finish its import and material checks first, then apply your capture-time contract. This is a downstream workflow suggestion, not a claim that Goblin3D provides this offline video exporter.[8]

The practical goal is modest: make a frame's intended state explicit and separate it from how slowly the computer produced it. Verify the actual video afterward. Smooth playback alone is not a performance benchmark.

Evidence used

Sources

  1. 1.Creator explanation of virtual-clock capture — Xiao. Accessed 2026-10-14.
  2. 2.Pinned walkthrough capture implementation — Crewship authors. Accessed 2026-10-14.
  3. 3.Pinned walkthrough workflow — Crewship authors. Accessed 2026-10-14.
  4. 4.Crewship license at the inspected revision — Crewship authors. Accessed 2026-10-14.
  5. 5.requestAnimationFrame timing — MDN Web Docs. Accessed 2026-10-14.
  6. 6.Canvas captureStream behavior — MDN Web Docs. Accessed 2026-10-14.
  7. 7.Manual canvas frame requests — MDN Web Docs. Accessed 2026-10-14.
  8. 8.Goblin3D public features — Goblin3D. Accessed 2026-10-14.

Prepare a prop for your next scene

Start with Goblin3D Image to 3D, then validate the exported asset and capture workflow in your own application.

Explore Image to 3D

Sources, product facts, and original evidence were checked before publication.

Share your feedback

Sign in to share feedback with the Goblin3D team.

Sign in

support@goblin3d.ai