Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Performance benchmarking skill — baseline and compare page load times, Core Web Vitals, bundle sizes, and API response times. Use when the user wants to measure performance before and after a change, catch performance regressions in a PR, establish a performance baseline, or understand what is making their app slow.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 147% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 449% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 143% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 316% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 146% | 0% |
Benchmarking without a baseline is noise. A benchmark that can't be reproduced is a guess. Every benchmark in this skill produces a number, a method, and a comparison — not an impression.
Performance is a feature. Regressions ship silently. This skill makes regressions visible before they reach users.
Before any optimization or PR review, establish the baseline. Without it, "after" numbers are meaningless.
bash# Run Lighthouse baseline (3 runs, take median) npx lighthouse https://your-app.com/key-page \ --output json \ --output-path ./baseline.json \ --chrome-flags="--headless" \ --throttling-method=simulate \ --preset=desktop # Run 3 times and average — single runs are noisy for i in 1 2 3; do npx lighthouse https://your-app.com/key-page \ --output json \ --output-path "./baseline-run-$i.json" \ --chrome-flags="--headless" done
Record in benchmark-baseline.md:
markdown## Baseline — [Page/Endpoint] — [Date] — [Commit SHA] | Metric | Run 1 | Run 2 | Run 3 | Median | |--------|-------|-------|-------|--------| | LCP | | | | | | INP | | | | | | CLS | | | | | | FCP | | | | | | TTFB | | | | | | Total JS bundle | | | | | | Total CSS bundle | | | | | | Network requests | | | | | **Environment:** macOS M2 / 6-core CPU throttle / Fast 3G simulation **Tool:** Lighthouse 12.x / Chrome 124 **URL:** https://your-app.com/key-page
| Metric | Full Name | Measures | Good | Needs Improvement | Poor | |--------|-----------|----------|------|-------------------|------| | LCP | Largest Contentful Paint | Loading | ≤2.5s | 2.5–4.0s | >4.0s | | INP | Interaction to Next Paint | Interactivity | ≤200ms | 200–500ms | >500ms | | CLS | Cumulative Layout Shift | Visual stability | ≤0.1 | 0.1–0.25 | >0.25 | | FCP | First Contentful Paint | Loading start | ≤1.8s | 1.8–3.0s | >3.0s | | TTFB | Time to First Byte | Server response | ≤800ms | 800ms–1.8s | >1.8s |
bash# Single page audit npx lighthouse https://your-app.com \ --output=json,html \ --output-path=./lighthouse-report \ --chrome-flags="--headless --no-sandbox" # Key metrics to extract from JSON cat lighthouse-report.report.json | jq '{ lcp: .audits["largest-contentful-paint"].numericValue, inp: .audits["interaction-to-next-paint"].numericValue, cls: .audits["cumulative-layout-shift"].numericValue, fcp: .audits["first-contentful-paint"].numericValue, ttfb: .audits["server-response-time"].numericValue, score: .categories.performance.score }'
bash# Next.js npx next build 2>&1 | grep -A 50 "Route (app)" # Webpack Bundle Analyzer npm install --save-dev webpack-bundle-analyzer # Add to webpack config, then: npx webpack --profile --json > stats.json npx webpack-bundle-analyzer stats.json # Source map explorer (any bundler) npm install --save-dev source-map-explorer npx source-map-explorer 'build/static/js/*.js' # Check for duplicate packages npx duplicate-package-checker-webpack-plugin
bash# Before (on main branch) git checkout main && npm run build du -sh .next/static/chunks/*.js | sort -rh | head -10 > bundle-before.txt # After (on feature branch) git checkout feature-branch && npm run build du -sh .next/static/chunks/*.js | sort -rh | head -10 > bundle-after.txt diff bundle-before.txt bundle-after.txt
javascript// k6-script.js import http from 'k6/http'; import { check, sleep } from 'k6'; export const options = { stages: [ { duration: '30s', target: 10 }, // ramp up { duration: '1m', target: 10 }, // steady state { duration: '10s', target: 0 }, // ramp down ], thresholds: { http_req_duration: ['p(95)<500'], // 95% of requests under 500ms http_req_failed: ['rate<0.01'], // <1% error rate }, }; export default function () { const res = http.get('https://your-app.com/api/endpoint', { headers: { Authorization: `Bearer ${__ENV.API_TOKEN}` }, }); check(res, { 'status is 200': (r) => r.status === 200, 'response time < 200ms': (r) => r.timings.duration < 200, }); sleep(1); }
bash# Run benchmark k6 run k6-script.js # Compare before/after k6 run --out json=before.json k6-script.js # Make the change k6 run --out json=after.json k6-script.js
bash# Measure individual request timing curl -o /dev/null -s -w \ "TTFB: %{time_starttransfer}s\nTotal: %{time_total}s\nSize: %{size_download} bytes\n" \ https://your-app.com/api/endpoint # Run 10 times and get stats for i in $(seq 1 10); do curl -o /dev/null -s -w "%{time_total}\n" https://your-app.com/api/endpoint done | awk '{sum+=$1; if(NR==1||$1<min)min=$1; if($1>max)max=$1} END {printf "min=%.3fs avg=%.3fs max=%.3fs\n", min, sum/NR, max}'
sql-- Explain analyze a slow query EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT) SELECT u.*, o.total FROM users u JOIN orders o ON o.user_id = u.id WHERE u.created_at > NOW() - INTERVAL '30 days' ORDER BY o.total DESC LIMIT 100; -- Check for sequential scans on large tables (should be index scans) -- Look for: Seq Scan on large_table — this is the red flag -- Find slow queries in PostgreSQL SELECT query, calls, mean_exec_time::numeric(10,2) AS mean_ms, max_exec_time::numeric(10,2) AS max_ms, total_exec_time::numeric(10,2) AS total_ms FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 20;
Always produce this table when benchmarking a change:
markdown## Benchmark Results — [Feature/Change] — [Date] **Commit before:** abc1234 **Commit after:** def5678 **Environment:** [describe exactly] **Tool:** [Lighthouse 12 / k6 1.0 / custom] ### Core Web Vitals (median of 3 runs) | Metric | Before | After | Delta | Verdict | |--------|--------|-------|-------|---------| | LCP | 2.8s | 2.1s | -25% | ✅ Improved | | INP | 180ms | 210ms | +17% | ⚠️ Regressed | | CLS | 0.05 | 0.05 | 0% | ✅ Neutral | | Perf score | 72 | 81 | +9pts | ✅ Improved | ### Bundle Size | Asset | Before | After | Delta | Verdict | |-------|--------|-------|-------|---------| | main.js | 284KB | 301KB | +6% | ⚠️ Regressed | | vendor.js | 412KB | 398KB | -3% | ✅ Improved | | CSS total | 48KB | 48KB | 0% | ✅ Neutral | ### API Response Time (p50 / p95 / p99) | Endpoint | Before | After | Delta | Verdict | |----------|--------|-------|-------|---------| | GET /api/users | 45/120/280ms | 38/95/210ms | -25% | ✅ Improved | | POST /api/orders | 120/380/950ms | 125/390/960ms | +1% | ✅ Neutral | ### Overall Verdict 🟢 SHIP — performance improved overall. INP regression is within acceptable range (180→210ms, still <200ms threshold). main.js increase (+17KB) is offset by lazy-loading the settings panel.
Define these per project. These are sensible defaults:
| Metric | Warning threshold | Block threshold | |--------|------------------|-----------------| | LCP | +10% | +25% or >4.0s | | INP | +15% | +30% or >500ms | | CLS | any increase | >0.1 absolute | | JS bundle | +5% | +15% or +50KB | | API P95 | +10% | +25% or >1s | | Lighthouse score | -3pts | -10pts |
yaml# .github/workflows/lighthouse.yml name: Lighthouse CI on: [pull_request] jobs: lighthouse: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 - run: npm ci && npm run build - run: npm run start & - uses: treosh/lighthouse-ci-action@v11 with: urls: | http://localhost:3000/ http://localhost:3000/dashboard budgetPath: ./lighthouse-budget.json uploadArtifacts: true # lighthouse-budget.json [{ "path": "/*", "timings": [ { "metric": "largest-contentful-paint", "budget": 2500 }, { "metric": "cumulative-layout-shift", "budget": 0.1 } ], "resourceSizes": [ { "resourceType": "script", "budget": 400 }, { "resourceType": "total", "budget": 800 } ] }]
Use python scripts/compare_metrics.py baseline.json candidate.json to compare flat JSON metric files in a repeatable way. Read metric-contract.md first to define units and regression thresholds; do not compare incompatible measurements.
benchmark-results/ for historical comparisonOther measured skills in the registry, with their headline benchmark lift.