Execution Guide

Tool versions

ToolVersion usedNotes
Node.js23.11.0any ≥ 18 with global fetch/ESM works
playwright-core1.62.1ships no browsers; see below
Chrome for Testing149.0.7827.55 (arm64)reused from an existing Playwright cache
Python3.13only for python3 -m http.server in the manual labs

playwright-core deliberately ships no browser binaries. src/lib/browser.mjs resolves one in this order:

  1. $CHROME environment variable, if set
  2. newest chromium-NNNN in ~/Library/Caches/ms-playwright (or ~/.cache/ms-playwright)
  3. a system Chrome/Chromium in the usual install locations

If none is found it fails with instructions rather than silently using a different engine.

Version sensitivity. Several findings here are version-dependent — scheduler.yield() availability, LoAF support, and await tick cost have all changed within recent memory. Record the version with any result you keep. docs/measured-results.md states the version used for every number in this module.

Quick start

cd fe-01-execution-model-scheduling/src
npm install                 # playwright-core only, ~1 package
npm run ordering            # experiments A, B, C  (~40s)
npm run starvation          # experiment D          (~5s)
npm run layout              # experiment E          (~5s)
npm run frame-timing        # experiment F          (~10s)
npm run benchmark           # the 7-strategy lab    (~90s)

Or everything in sequence:

npm run all

Manual (browser) labs

Two labs are run by hand in a real browser, because the point is what you observe:

npm run serve               # http://localhost:8080
URLPurpose
/starvation.htmlStep 2 — feel the difference between microtask and task chunking
/lab.htmlStep 4 — implement the strategies yourself (TODO stubs)

web/bugs.js holds the failure lab (F1–F3) and is loaded from the console; see steps/05-failure-lab.md.

Tuning the benchmark

All parameters are environment variables:

ROWS=50000    npm run benchmark    # smaller dataset — find the worker crossover
THROTTLE=1    npm run benchmark    # no CPU throttling (see warning below)
THROTTLE=20   npm run benchmark    # low-end device simulation
REPS=5        npm run benchmark    # more repetitions, tighter medians
QUERY=zulu    npm run benchmark    # different selectivity
TRIALS=100    npm run ordering     # more ordering trials
WORK_MS=2000  npm run starvation   # longer starvation window
COUNT=3000    npm run layout       # more elements to thrash

Throttling is not optional for meaningful results. THROTTLE=1 on a modern laptop produces numbers that describe your laptop, not your users. The default of 6× approximates a mid-tier phone. Every table in this module was produced at 6×.

Reproducibility notes

  • The dataset uses a seeded PRNG, so row contents are identical across runs and machines.
  • Keystrokes are dispatched through CDP as trusted input events, not dispatchEvent — which matters here specifically, because synthetic dispatch changes microtask interleaving (experiment B).
  • The benchmark reports medians of REPS runs. Single runs vary by 30–50 % on the wall-clock columns; the ratios between strategies are the durable finding, not the absolute milliseconds.
  • Headless frame pacing is not real display pacing. Treat the jank column as directional.

Cleaning up

Nothing is installed globally and no state persists outside src/node_modules.

rm -rf src/node_modules