Real browser metrics → causal degradation graph → actionable report. Built for AI agents, useful for humans.
PerfGraph launches a headless Chromium browser, captures performance data via Chrome DevTools Protocol, runs it through a 5-stage analysis pipeline, and spits out a structured JSON report with issues sorted by severity, causal chains, and prioritized fixes.
npx perfgraph run --url https://example.com --prettyPerfGraph — not a symptom list, a diagnosis.
Root cause → impact → fix, in one graph.
Lighthouse gives you a score. PerfGraph tells you why it's bad and what to fix first.
Instead of digging through a 10k-line trace.json or a wall of Lighthouse audits, you get a focused report with root causes linked to impact. The output is designed to be read by AI agents (or you) without a decoder ring.
collect → normalize → extract → analyze → report
| Step | What it does |
|---|---|
| Collect | Launches headless Chromium, captures network, trace, performance API, JS coverage, console logs, DOM snapshot, and Lighthouse audit |
| Normalize | Validates raw data through Zod schemas into a unified Intermediate Representation (IRBundle). All timestamps normalized, data cleaned |
| Extract | Computes 7 diagnostic feature sets from the IR: LCP breakdown, critical network chain, main-thread blocking time, JS hotspots, layout shifts, third-party overhead, render-blocking score |
| Analyze | Runs 30+ causal rules against the features, builds a directed graph where edges represent known causal relationships with confidence levels (strong/medium/weak) |
| Report | Produces a self-contained JSON report with issues sorted by severity, causal chains from root cause to user impact, and prioritized recommendations with expected impact estimates |
npm install -g perfgraphOr skip the install:
npx perfgraph --helpRequirements: Node.js ≥ 22, Chromium (Playwright installs it automatically on first run).
Full pipeline, one command:
perfgraph run --url https://example.com --prettyStep by step:
# 1. Collect data
perfgraph collect --url https://example.com --output ./results
# 2. Normalize → Extract → Report
perfgraph normalize ./results/perfgraph_example_20260609_120000 --output ir.json
perfgraph extract ir.json --output features.json
perfgraph report features.json --output report.json --prettyFull pipeline in one shot.
--url <url> Required. Target URL to analyze.
--output <dir> Output directory (default: ./perfgraph-output).
--runs <n> Number of collection runs (default: 1).
--pretty Pretty-print the final report JSON.
--device <name> Mobile emulation (e.g. "iPhone 13").
--no-lighthouse Skip Lighthouse collection.
--no-coverage Skip JS/CSS coverage.
--no-console Skip console log capture.
--no-dom Skip DOM snapshot.
Captures performance data from a URL. See run options — same flags apply.
Converts raw collected data into a validated IRBundle. Accepts a run directory or a parent directory (auto-detects latest run).
--output, -o <file> Write to file (default: stdout).
--pretty Pretty-print JSON.
Computes diagnostic features from a normalized IR bundle.
--input, -i <file> Path to IR JSON (alternative to positional).
--output, -o <file> Write to file (default: stdout).
--pretty Pretty-print JSON.
Applies causal rules and builds a degradation graph.
--input, -i <file> Path to FeatureSet JSON (alternative to positional).
--output, -o <file> Write to file (default: stdout).
--pretty Pretty-print JSON.
Generates the final performance report. Accepts a FeatureSet JSON (from extract) — runs the causal engine internally, no need to call analyze separately.
--input, -i <file> Path to FeatureSet JSON (alternative to positional).
--output, -o <file> Write to file (default: stdout).
--pretty Pretty-print JSON.
Starts an MCP stdio server for AI agent integration. No flags. See AGENTS.md for details.
The report is a single JSON file. Key sections:
| Category | Issues |
|---|---|
| LCP | Slow LCP, high TTFB, render-blocking resources, LCP resource delay chains |
| JavaScript | Long tasks, high TBT, heavy execution, unused code, main-thread bottlenecks |
| Network | Deep request chains, bandwidth bottlenecks, waterfall depth, critical path analysis |
| Layout | Layout shifts (CLS), large DOM size, forced reflows |
| Third-party | Third-party script overhead, tracking pixels, embedded widget impact |
src/
├── index.ts CLI entry — lazy-loads commands
├── collect/ CDP data collection via Playwright
│ ├── browser.ts Browser launcher
│ ├── collector.ts Orchestrator
│ ├── coverage.ts JS/CSS coverage
│ ├── network.ts Network request capture
│ ├── performance.ts Performance API metrics
│ ├── runtime.ts Runtime metadata
│ ├── dom.ts DOM snapshot
│ └── lighthouse.ts Lighthouse audit
├── normalize/ Data normalization & IR validation
├── extract/ 7 diagnostic feature extractors
├── causal/ Causal rule engine (30+ rules)
│ ├── builder.ts Graph construction
│ ├── rules/ Individual causal rules by category
│ └── types.ts Graph data types
├── report/ Report generation & scoring
│ ├── analyzer.ts Report builder
│ ├── scorer.ts Score computation
│ ├── remediations.ts Remediation templates
│ └── types.ts Report schema
├── distill/ Agent-optimized summary layer (insights.json)
├── mcp/ MCP stdio server
├── cli/ CLI command handlers
└── shared/ Shared utilities & types
npm install
npm run build # compile TypeScript
npm run typecheck # check types only
npm test # run tests
npm run test:watch # tests in watch mode
npm run dev # tsx watch — no build step| Thing | What |
|---|---|
| Runtime | Node.js ≥ 22 |
| Language | TypeScript (strict, noUncheckedIndexedAccess) |
| Browser | Playwright (Chromium) |
| Validation | Zod at every data boundary |
| Graph engine | @dagrejs/graphlib |
| Performance audits | Lighthouse |
| Testing | Vitest |
MIT
{ "meta": { "url": "https://example.com", "analyzedAt": "2026-06-09T19:15:19.000Z", "reportVersion": "1.0.0", "featureCount": 7, "graphNodeCount": 24, "graphEdgeCount": 31, "ruleCount": 32, }, "summary": { "score": "moderate", // "good" | "moderate" | "poor" "criticalIssues": 2, "warnings": 5, "infos": 3, "topIssues": [ { "id": "js-long-task", "label": "Long task", "severity": "critical", "confidence": "strong", }, ], }, "issues": [ { "id": "lcp-slow", "label": "LCP exceeds 2.5s threshold", "severity": "critical", "value": 4320, "unit": "ms", "threshold": 2500, "confidence": "strong", "remediation": "Optimize largest contentful paint element...", "chainId": "lcp:3", }, ], "chains": [ { "id": "lcp:3", "rootCause": "LCP > 2.5s", "impact": "Poor user experience", "path": [ "TTFB delayed by server response", "Render-blocking stylesheets", "LCP element render delay", ], "length": 3, }, ], "recommendations": [ { "priority": "critical", "category": "LCP", "title": "Optimize Largest Contentful Paint", "action": "Inline critical styles, defer non-critical CSS", "expectedImpact": "Reduces LCP by ~40%", "relatedIssues": ["lcp-slow"], }, ], "features": { /* raw extracted features for cross-referencing */ }, }