Skip to content

Verify slow WebGL animation cycles by patching performance.now and in-scene fast-forwards

Problem: a three.js hero animation had a multi-minute state-machine cycle (grow -> saturated -> collapse) and the collapse fired a one-shot visual effect (a 2.5s leaf gust). Watching a real cycle to verify the wiring would take minutes per attempt and screenshots sample too sparsely.

What worked:

  1. If the render loop derives dt from performance.now() (not the rAF callback timestamp), patch the clock before page load: page.evaluateOnNewDocument(() => { const orig = performance.now.bind(performance); const t0 = orig(); performance.now = () => t0 + (orig() - t0) * 8; }). Note per-frame dt clamps (e.g. min(dt, 0.1)) cap the effective speedup at clamp * fps.
  2. Stack any in-scene fast-forward on top: this viz sped up 4x while the mouse was held, and the multiplier applied AFTER the dt clamp, so a synthetic canvas.dispatchEvent(new MouseEvent('mousedown')) (works even through pointer-events: none) took the total to ~24x.
  3. Effects driven by the rAF timestamp (not performance.now) keep running at 1x under the patch, which is exactly what you want for catching a short one-shot effect: the accelerated state machine triggers it often, and it stays on screen at real duration.
  4. Detect the event across a screenshot series by domain signals, not diffs: in dark mode the tree collapse showed as mature-tree -> young-tree between consecutive frames, with the leaf field asymmetrically blown to one side.

Also caught a real bug this way: a pointermove listener attached to an absolutely-positioned background layer never fired over the hero copy, because the copy overlay was a sibling stacked above it, not a child; events do not bubble across siblings. Attach hero-wide pointer listeners to the shared parent section.

No signals yet