Thanks for taking an interest. This is a small, deliberately narrow tool, and the goal is to keep it that way: measure scroll performance accurately, explain the result, and fail a build when it regresses.
git clone https://github.com/css-scroll-driven/scroll-jank-profiler.git
cd scroll-jank-profiler
npm install
npm testYou need Node.js 20 or newer and your own Chrome or Chromium installation. npm install pulls
puppeteer-core and nothing else — it will not download a browser.
To try a change end to end:
npm run demo:serve
node bin/scroll-jank-profiler.mjs --url http://localhost:8000/janky.html --verbose
node bin/scroll-jank-profiler.mjs --url http://localhost:8000/optimised.html --verboseBoth demo pages should keep behaving as advertised: janky.html fails most checks, and
optimised.html reports no forced reflows, no long tasks and no non-compositor animations.
One dependency. puppeteer-core is the only runtime dependency, and that is a deliberate
constraint rather than an accident. Colour output, argument parsing and report rendering are all
hand-rolled. Please do not add packages; if something genuinely cannot be done without one, open an
issue to discuss it first.
Analysers stay pure. Everything under src/analyze/ must be a pure function over trace-event
arrays and plain data. No browser handles, no file I/O, no clock reads. This is what makes the
analysis layer testable and what lets people re-analyse saved traces.
Every check explains itself. A finding that a user cannot act on is noise. Each check carries a plain-language description of what it means and a link to an article explaining how to fix it. New checks need both.
Node ESM, Node 20+. No transpilation, no build step, no CommonJS.
JSDoc on anything exported. Types are expressed as JSDoc rather than TypeScript.
No dead code. If a branch is unreachable or an export is unused, remove it.
- Write or extend a pure analyser in
src/analyze/. - Add a fixture in
tests/fixtures/and tests intests/. - Add an entry to the
CHECKSarray insrc/budget.js, with awhystring and alinkkey fromsrc/links.js. - Add a default threshold to
DEFAULT_BUDGETand toscroll-budget.example.json. - Add a row to the checks table and the budget schema table in
README.md.
The reporters read from the analysis and the budget report generically, so they usually need no change at all.
Fixtures are small, hand-authored JSON arrays of trace events, committed under tests/fixtures/.
Keep them minimal and readable — the point is that a reviewer can see exactly what a test asserts
without opening a 40MB capture.
If you are fixing a bug caused by a real trace shape Chrome emits, add the smallest fixture that
reproduces it, and note the Chrome version in a comment on the pull request. Chrome has moved trace
payloads around between versions more than once; pipelineState() in src/analyze/frames.js is
the scar tissue from that.
npm testTests use the built-in node --test runner, and must pass without a browser. Anything that needs
Chrome belongs in a manual verification step, not the test suite.
- One logical change per pull request.
- Include the reasoning, not just the diff — especially for anything that changes a measurement, since that silently changes every downstream budget.
- If a change alters what a number means, say so plainly in the pull-request description and update
CHANGELOG.md. - Run
npm testand a manual profile of both demo pages before opening the PR.
Please include the Chrome version, the operating system, the exact command line, and — if the
problem is a wrong measurement — the JSON report from --reporter json. A trace fixture that
reproduces it is the most useful thing you can attach.
By contributing you agree that your contributions are licensed under the MIT License.