Should a Three.js animation use Clock.getDelta() or wall-clock timestamps for long-running scenes?
0 reputation · 05 Nov 2020, 17:38 UTC
0 reputation · 05 Nov 2020, 17:38 UTC
I'm designing a Three.js scene where some animations are frame-driven (rotations, shader time uniforms) and others are event-driven, meaning an object must reach a specific state at a specific real-world moment (for example, syncing a visualization to a scheduled timestamp from an API).
My understanding is that THREE.Clock provides getDelta() and getElapsedTime() for frame-rate independent motion, while requestAnimationFrame also passes a high-resolution timestamp to its callback. For anything tied to actual calendar time, the native JavaScript Date object seems to be the only option, since Three.js has no built-in date or time zone handling.
Two concerns are unresolved for me. First, whether accumulating delta values over a very long session (hours or days) introduces meaningful floating-point drift compared to reading elapsed time directly. Second, whether mixing Clock-based deltas for motion with Date-based deadlines for scheduled state changes can cause visible desynchronization when the tab is throttled or suspended in the background.
Which time source is the documented, reliable choice for each of these two cases? Is there a recommended pattern in Three.js for reconciling accumulated animation time with absolute wall-clock deadlines? And does background tab throttling require any special handling to keep both in sync?
35275 reputation · 06 Nov 2020, 00:13 UTC
Use THREE.Clock.getDelta() (which internally relies on performance.now()) for all frame‑driven motion such as rotations, shader time uniforms, and physics steps. Use Date.now() (or a stored UTC epoch captured at start) for any event that must fire at a specific real‑world moment, e.g. a scheduled API timestamp.
requestAnimationFrame callbacks. Accumulating these deltas over hours or days adds only microseconds of floating‑point drift, which is negligible for visual animation.// One‑time setup
const startPerf = performance.now();
const startUtc = Date.now();
const clock = new THREE.Clock();
function animate() {
const delta = clock.getDelta(); // frame‑rate independent motion
const nowUtc = Date.now();
const elapsedReal = nowUtc - startUtc; // real‑world elapsed ms
// Example frame‑driven update
mesh.rotation.y += rotationSpeed * delta;
// Example scheduled event
if (elapsedReal >= deadlineMs) {
triggerScheduledEvent();
}
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
When a tab is backgrounded, browsers throttle or pause requestAnimationFrame. During the pause clock.getDelta() returns zero, so animation time stops advancing — this keeps visual state consistent with the frames that actually ran. Meanwhile Date.now() continues ticking, so elapsedReal will jump ahead.
If the gap matters (e.g., a deadline must fire exactly at the wall‑clock moment), detect a long pause and decide whether to catch up the animation or simply let the deadline trigger on resume:
let lastPerf = performance.now();
const PAUSE_THRESHOLD_MS = 10_000; // 10 s
function animate() {
const currentPerf = performance.now();
const pauseMs = currentPerf - lastPerf;
lastPerf = currentPerf;
if (pauseMs > PAUSE_THRESHOLD_MS) {
// Tab was suspended – choose a policy:
// 1) Let animation stay paused (default Clock behavior)
// 2) Fast‑forward animation by pauseMs / 1000 seconds
// clock.start(); // resets internal startTime to now
}
const delta = clock.getDelta();
const nowUtc = Date.now();
const elapsedReal = nowUtc - startUtc;
// … rest of loop
}
startPerf and startUtc at load. In each frame log clock.getElapsedTime() and (Date.now() - startUtc) / 1000. After an hour the difference should be < 5 ms.clock.getDelta() was near zero during the pause and that elapsedReal advanced by ~5 minutes.performance.now() over days is well below 1 ms; sub‑millisecond precision needs (rare for visual work) may require periodic re‑sync.Does your scheduled event need to fire *exactly* at the wall‑clock moment even if the tab was suspended, or is it acceptable for the event to fire on the next frame after resume? The answer determines whether you must fast‑forward animation state on resume.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.