Conversation
Opt-in --virtual-time-budget-ms: when the page is idle except for pending timers, jump the page clock to the next scheduler task instead of sleeping, up to a per-navigation budget, then fall back to real time. Timers fire in order and in-flight transfers are never skipped. Timers, performance.now, event timestamps, resource timing, the network-idle hold and the intersection burst guard read the page clock; the watchdog, HTTP client, rate limiter and server keep the boot clock. Date.now stays real until the V8 platform clock is offset too. Serve is unaffected.
Each virtual time jump also moves the offset on V8's wall clock, through the fork's v8__Platform__SetClockOffsetMillis, so Date.now() keeps pace with the timers it drives. Pins zig-v8-fork at lightpanda-io/zig-v8-fork#203; the prebuilt archive needs a tag bump once that lands.
karlseguin
force-pushed
the
virtual-time
branch
from
September 11, 2026 09:13
a211989 to
0695e28
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Opt-in
--virtual-time-budget-ms <INT>(common option;fetch,mcp,agent): when the page is idle except for pending timers, advance the page's clock to the next scheduler task instead of sleeping, up to a per-navigation budget, then fall back to real time. Same model as Chrome's--virtual-time-budgetwithpauseIfNetworkFetchesPending.Rules
Runner._tick's idle return, plus the idle arms ofwaitForSelector/waitForScript) is the one place the clock moves.Clocks
src/browser/VirtualTime.zigis the page's clock: scheduler due times,performance.now, event timestamps, resource timing, the network-idle hold and the intersection burst guard read it. Watchdog, HTTP client, rate limiter and server stay on the boot clock.Date.now()follows too, throughv8__Platform__SetClockOffsetMillisfrom lightpanda-io/zig-v8-fork#203: debounce/throttle helpers re-armsetTimeoutagainstDate.now()and would spin until real time caught up otherwise.Scope
Serve/CDP is deliberately untouched: the CDP loop never reaches the idle sleep (
.doneis inert there and the driver fd keeps the HTTP client polling), soEmulation.setVirtualTimePolicywould be a separate change. Ignored-by-serve is documented in the help text.On a page that keeps fetching (eu.gymshark.com's listing fires an Algolia request as soon as the previous one returns) the budget never engages, by design. It targets pages that wait on timers after the network quiets.
Numbers
ReleaseFast build against a release V8 carrying the fork shim,
fetch --dump html --wait-until done --wait-ms 15000, with and without--virtual-time-budget-ms 30000:src/browser/tests/runner/virtual_time_clocks.html)MDN waits on three 1 s timers after the network quiets; the dumps differ only in per-render Lit markers and ad rotation. On the fixture
performance.now,Event.timeStampandDate.nowall report 3000 ms inside the callback in both modes. Gymshark never goes network-idle, so the clock never moves. Pages that re-arm polling timers forever (bbc.com, web.dev) drain the budget without reachingdone, as without the flag.Depends on
lightpanda-io/zig-v8-fork#203, pinned by commit in
build.zig.zon. CI needs a fork tag with the new export before this links; then bumpzig-v8in.github/actions/install/action.ymland repointbuild.zig.zonat the merge commit.