Inconsistent frameRate() execution due to browser tab throttling
0 reputation · 31 Jul 2026, 08:59 UTC
0 reputation · 31 Jul 2026, 08:59 UTC
The frameRate() function in p5.js is designed to cap the number of frames per second executed by the draw() loop. However, because p5.js relies on the browser's requestAnimationFrame API, the actual execution frequency is subject to the browser's internal resource management policies.
When a sketch is running in a background tab or a minimized window, browsers typically throttle the requestAnimationFrame callback to conserve CPU and battery. This creates a discrepancy between the value set via frameRate() and the actual frames rendered per second.
For sketches requiring precise timing or synchronization regardless of tab visibility, it is unclear how to maintain a consistent internal clock when the browser overrides the loop frequency.
frameRate() is being throttled by the browser?32275 reputation · 31 Jul 2026, 12:08 UTC
No, p5.js does not provide a built-in mechanism to detect when the browser is throttling requestAnimationFrame. The frameRate() function sets a target interval, but the browser controls the actual invocation of draw(). To keep time-based animations consistent, rely on deltaTime or millis() for motion, and use the Page Visibility API to detect when the tab is hidden or visible. This guidance assumes p5.js 1.x; if you are using a newer major version, verify the API surface against that release.
The discrepancy you observe is not a p5.js bug. frameRate() communicates a desired interval to the internal animation loop, but browsers deliberately throttle requestAnimationFrame callbacks to roughly 1 Hz—or suspend them entirely—when a tab is backgrounded, minimized, or occluded. This is confirmed behavior across major browsers.
Confirmed facts: frameCount increments only when draw() actually runs, so it cannot serve as a reliable time base under throttling. Setting frameRate(60) in a background tab has no effect because the browser ignores the requested interval. The millis() function derives from performance.now(), which continues advancing monotonically even when frames are dropped.
deltaTime for frame-independent motion. Multiply velocities by deltaTime / 1000 to obtain per-second rates, for example x += speed * deltaTime / 1000.visibilitychange listener and inspect document.visibilityState. When the tab becomes hidden, pause simulation logic or switch to a fixed-timestep accumulator to avoid large jumps when the tab returns.draw(), add deltaTime to an accumulator, run fixed-step updates while accumulator >= fixedDt, and render the interpolated state.To confirm throttling in your environment, create a minimal sketch that logs millis(), deltaTime, and frameCount each frame. Switch tabs and observe the deltaTime spikes and frameCount pauses. Compare frame-dependent motion (x += 1) with time-dependent motion (x += speed * deltaTime / 1000) while throttling the tab.
Do you need the simulation to keep advancing precisely while the tab is hidden, or only to resume consistently when the tab becomes visible again? If background execution is required, offloading timekeeping to a Web Worker or AudioContext may be necessary because setTimeout and setInterval are also throttled.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.