Skip to content
→ All work

Case study 37 / 37

Waterfall Lens

Turns a HAR capture into an answer: the critical rendering chain, connection queuing and third-party weight, explained in plain English beside an interactive waterfall.

Status
In development
Period
2026
Domain
Web · Tools
Language
TypeScript
Last push
26 OCT 2026
License
MIT
Source of claims
README.md, CHANGELOG.md, docs/PRODUCTION_READINESS.md, src/analyze.ts, test/hostile-input.test.ts, .github/workflows/ci.yml
waterfall-lens report with summary metrics and first-paint explanation

01/The problem

DevTools already draws a waterfall. What it does not say is why first paint was slow and whose fault it was. A page can make 80 requests while only three gate first paint.

02/The system

A TypeScript CLI and library. A pure analysis function walks real initiator links back from the last render-blocking response to the navigation request, separates time spent queued for a connection from network time, and attributes page weight to third-party origins. The report is one self-contained HTML file that works from file://.

Analysis pipeline (README)7 components
  • parser.ts to normalizeEntries
  • normalizeEntries to buildCriticalChain
  • normalizeEntries to Third-party weight
  • normalizeEntries to Queued requests
  • buildCriticalChain to cli.ts
  • buildCriticalChain to html.ts

03/Implementation

  1. 01Structural HAR validation with clear errors for truncated or non-HAR JSON.
  2. 02Entry normalisation: resource type, timings relative to navigation start, third-party and render-blocking flags.
  3. 03Critical chain built by walking _initiator links backward from the latest-finishing render-blocking response.
  4. 04Queued-request detection that separates connection-slot waiting (blocked time with no DNS, connect or TLS) from genuinely slow responses.
  5. 05Third-party weight grouped by host, and a plain-English explanation of all of the above.

04/Engineering

A heuristic, labelled as one

HAR has no async/defer metadata, so a script counts as render-blocking only when DevTools recorded it as parser-discovered. This is documented in the source and not presented as a browser simulation.

Engine without I/O

analyze.ts is a pure Har → WaterfallReport function, so most tests run against hand-built HAR shapes.

HAR strings are untrusted

The report inserts every HAR string as escaped text and escapes < in embedded JSON; a test renders HTML and script payloads in URLs and checks nothing executes.

05/Interface

The HTML report for the bundled news-article HAR: summary stats, a plain-English explanation of what delayed first paint, and the critical chain.
The interactive waterfall with resource-type colours, the critical request outlined, and the third-party weight table.
Clicking the third-party ad tag highlights its initiator chain, and the tooltip shows timing, size and what initiated it.

Real screenshots of the running software. Captions state the data shown.

06/Tech stack

  • TypeScript
  • Node.js 20+
  • HAR 1.2
  • Vanilla JS report
  • Vitest
  • GitHub Actions

07/Result

Verified outcomes

  • 118 Vitest tests with enforced coverage thresholds, run by CI across Node 20/22 on Ubuntu, Windows and macOS (per the README).
  • Reported in the README (one run, Apple M5): a synthetic 20,000-entry capture analysed in about 0.2 s by the CLI.

Known limitations

  • Render-blocking classification is a heuristic; the whole HAR is read into memory.
  • The report's initiator lookups are quadratic in the number of entries.

08/Links

Related work

Next case study

DataJewellers →