Skip to content

Performance Measurement & Refinement

Gasoline captures Core Web Vitals, navigation timing, resource loading, and long tasks from every page your AI visits. Use this data to measure performance, identify bottlenecks, and verify optimizations — all without leaving your AI workflow.

Get a snapshot of Core Web Vitals for the current page:

observe({what: "vitals"})

Returns the latest metrics with Google’s standard ratings:

MetricWhat It MeasuresGoodNeeds ImprovementPoor
FCPFirst Contentful Paint — time to first visible content≤ 1.8s1.8–3.0s> 3.0s
LCPLargest Contentful Paint — time to main content visible≤ 2.5s2.5–4.0s> 4.0s
CLSCumulative Layout Shift — visual stability≤ 0.10.1–0.25> 0.25
INPInteraction to Next Paint — input responsiveness≤ 200ms200–500ms> 500ms

Each metric includes the raw value and a good / needs_improvement / poor rating.

  • vitals — quick check of the latest Core Web Vitals. Fast, focused, ideal for “how does this page perform right now?”
  • performance (via analyze) — full performance snapshots including navigation timing, network breakdown, long tasks, and resource details. Use this for deep analysis.

analyze({what: "performance"})

Returns all captured performance snapshots (up to 20 retained). Each snapshot includes:

MetricDescription
time_to_first_byteServer response time (TTFB)
first_contentful_paintFirst visible content rendered
largest_contentful_paintMain content visible
dom_interactiveDOM ready for interaction
dom_content_loadedDOMContentLoaded event fired
loadFull page load event fired
interaction_to_next_paintWorst-case input responsiveness

Aggregated resource loading data broken down by type:

CategoryWhat’s Counted
scriptJavaScript files
styleCSS files
imageImages (PNG, JPEG, SVG, WebP, etc.)
fontWeb fonts (WOFF2, WOFF, TTF)
fetchXHR and Fetch API calls
otherEverything else

Per category: request count, transfer size (compressed), decoded size (uncompressed).

Plus the slowest requests — the top resources by duration, so you immediately see what’s dragging down the waterfall.

Tasks that block the main thread for more than 50ms are captured:

MetricDescription
countNumber of long tasks during page load
total_blocking_timeSum of (task duration - 50ms) for each long task
longest_taskDuration of the single longest task

High total blocking time directly impacts INP. If your page has 500ms of blocking time, user interactions will feel sluggish.

If your application uses performance.mark() and performance.measure(), Gasoline captures those too:

// Your application code
performance.mark('api-call-start');
await fetchData();
performance.mark('api-call-end');
performance.measure('api-call', 'api-call-start', 'api-call-end');

These custom marks and measures appear in the performance snapshot alongside browser metrics. Up to 200 entries retained per type.


The most powerful performance feature: automatic before/after comparison when navigating or refreshing.

When you use interact to navigate or refresh:

interact({action: "refresh"})
interact({action: "navigate", url: "https://myapp.com/dashboard"})

Gasoline automatically:

  1. Stashes a before performance snapshot
  2. Executes the navigation or refresh
  3. Captures an after performance snapshot
  4. Computes a perf_diff — available in the async result

The perf_diff includes:

Verdict — one of:

  • improved — more metrics improved than regressed
  • regressed — more metrics regressed than improved
  • mixed — some improved, some regressed
  • unchanged — no meaningful change

Per-metric comparison:

FieldDescription
beforeValue before the action
afterValue after
deltaAbsolute change
pctPercentage change (“+50%”, “-10%“)
improvedWhether the change is better
ratingWeb Vitals rating (good/needs_improvement/poor)
unitms, KB, count, or empty for CLS

Resource changes:

  • Added — new resources not present before
  • Removed — resources that disappeared
  • Resized — same resource, size changed by >10% and >1KB
  • Retimed — same resource, load time changed significantly

Summary — human-readable description under 200 characters:

“LCP improved -25% (good); removed old-script.js; TRANSFER_KB improved -27%“

Add analyze: true to any DOM action to get performance profiling:

interact({action: "click", selector: "text=Load Dashboard", analyze: true})

This captures before/after snapshots around the action, showing the performance impact of that specific interaction.


See exactly how resources load:

observe({what: "network_waterfall"})
observe({what: "network_waterfall", limit: 50, url: "/api"})

Per resource entry:

FieldDescription
name / urlFull resource URL
initiator_typeWhat triggered the load (script, style, fetch, image, etc.)
durationTotal load time (ms)
start_timeWhen loading started (relative to page navigation)
transfer_sizeCompressed bytes over the network
decoded_body_sizeUncompressed bytes

Use this to identify:

  • Render-blocking resources — scripts and styles with early start_time and long duration
  • Oversized assets — large transfer_size relative to decoded_body_size (or vice versa — poor compression)
  • Waterfall bottlenecks — sequential chains where one resource depends on another
  • Redundant requests — same resource loaded multiple times

The waterfall data refreshes on demand — if it’s more than 1 second stale, Gasoline requests fresh data from the extension.


See performance events in context with everything else that happened:

observe({what: "timeline"})
observe({what: "timeline", include: ["network", "errors"]})

The timeline merges:

  • Network requests with timing and size
  • User actions (clicks, navigation, input)
  • Errors (console errors, exceptions)
  • WebSocket events (messages, connections)

Sorted chronologically, newest first. Default limit of 50 entries.

This is invaluable for understanding what caused a performance issue. If LCP spiked, the timeline shows what happened right before — maybe a failed API call triggered a retry cascade, or a user action loaded an expensive component.


Configure thresholds in a .gasoline.json file at your project root:

{
"budgets": {
"default": {
"load_ms": 2000,
"fcp_ms": 1800,
"lcp_ms": 2500,
"cls": 0.1,
"inp_ms": 200,
"ttfb_ms": 800,
"total_transfer_kb": 500,
"script_transfer_kb": 300
},
"routes": {
"/login": { "load_ms": 1000 },
"/dashboard": { "load_ms": 3000 }
}
}
}

When a performance snapshot exceeds a budget, Gasoline reports the violation:

{
"type": "budget_exceeded",
"url": "/dashboard",
"violations": [
{
"metric": "total_transfer_kb",
"budget": 500,
"actual": 620,
"over_by": "120KB (24%)"
}
]
}
PresetFCPLCPCLSINPLoadTTFB
web-vitals-good1800ms2500ms0.1200ms
web-vitals-needs-improvement3000ms4000ms0.25500ms
performance-budget-default3000ms600ms

Route-specific budgets override defaults using longest-prefix matching. The config file is reloaded every 30 seconds.


"Navigate to the dashboard and show me the Web Vitals."
interact({action: "navigate", url: "https://myapp.com/dashboard"})
observe({what: "vitals"})
"Show me the full performance snapshot — I want to see what's slow."
analyze({what: "performance"})

Look for:

  • High TTFB → server-side issue
  • Large gap between TTFB and FCP → render-blocking CSS/JS
  • High LCP with low FCP → main content loads late (lazy loading too aggressive, or hero image too large)
  • High CLS → layout shifts from dynamic content
  • High total blocking time → heavy JavaScript execution
"Show me the network waterfall, especially JavaScript files."
observe({what: "network_waterfall", url: ".js"})
"Refresh and compare performance."
interact({action: "refresh"})

The perf_diff tells you exactly what improved, what regressed, and by how much.

"Show me the timeline around the page load."
observe({what: "timeline", include: ["network"]})

Confirm that render-blocking resources are gone, API calls are parallelized, or whatever optimization you made is reflected.

"Generate a performance summary for the PR."
generate({format: "pr_summary"})

Produces a before/after comparison table suitable for pull request descriptions — Web Vitals deltas, resource changes, and a regression/improvement verdict.


Gasoline collects performance data passively through browser Performance APIs:

APIWhat It CapturesHow
PerformanceNavigationTimingPage load timing (TTFB, DOM events, load)Automatic on navigation
PerformanceResourceTimingPer-resource load timing and sizeAutomatic for all resources
PerformancePaintTimingFCPPerformanceObserver
LargestContentfulPaintLCPPerformanceObserver (continuously updated until interaction)
LayoutShiftCLSPerformanceObserver (excludes shifts within 500ms of user input)
Event timingINPPerformanceObserver (groups by interactionId, threshold 40ms)
PerformanceLongTaskTimingLong tasks (>50ms)PerformanceObserver
performance.mark() / measure()Custom application timingWrapped + observed

No configuration needed. As long as the extension is tracking a tab, performance data flows to the server automatically.


Compare across deploys: Navigate to the same page before and after a deploy. The perf_diff shows exactly what changed.

Use route-specific budgets: Your landing page should be fast (1s load). Your admin dashboard can be slower (3s). Set different budgets for different routes.

Watch total blocking time: TBT is the most actionable metric for JavaScript performance. If it’s high, find the long tasks and split or defer them.

Check transfer vs decoded size: If transfer_size is close to decoded_body_size, your server isn’t compressing responses. Enable gzip or brotli.

Profile specific interactions: Use analyze: true on clicks that trigger expensive operations (opening modals, loading data tables, switching tabs) to measure their impact.