Skip to content

Comments

ci(perf): Add Core Web Vitals measurement to benchmarks (INP, LCP, CLS)#39716

Open
MajorLift wants to merge 42 commits intomainfrom
jongsun/perf/260202-web-vitals
Open

ci(perf): Add Core Web Vitals measurement to benchmarks (INP, LCP, CLS)#39716
MajorLift wants to merge 42 commits intomainfrom
jongsun/perf/260202-web-vitals

Conversation

@MajorLift
Copy link
Contributor

@MajorLift MajorLift commented Feb 2, 2026

Description

Problem

Sentry collects ~19.5M pageload transactions from the MetaMask extension, but every one has zero performance measurements. browserTracingIntegration is active at 0.75% sample rate, yet produces empty transaction shells because Chrome does not emit paint/navigation timing entries on chrome-extension:// pages — no LCP, FCP, CLS, or TTFB.

Timer-based benchmarks (page load, confirm tx) measure specific operations but can't answer: Is the extension janky? Did this release regress interaction responsiveness? Which element causes the worst layout shift?

Solution

Add Core Web Vitals instrumentation via Google's web-vitals library, targeting two distinct environments:

1. Production (error correlation) — INP observers fire on chrome-extension:// pages (unlike LCP/CLS, INP uses PerformanceEventTiming which Chrome does support for extensions). When callbacks fire, they enrich the Sentry scope with rating tags (inp.rating:poor), attribution context (which element/interaction type), and breadcrumbs — attaching to existing transactions at zero additional cost. This lets us filter errors by performance context: "show me errors where the user was experiencing poor responsiveness."

2. CI benchmarks (regression detection) — All three metrics (INP, LCP, CLS) fire reliably in E2E benchmark runs. Per-run snapshots are collected via stateHooks, aggregated with the same IQR + z-score outlier detection used for timer statistics (with metric-specific sanity bounds — CLS is unitless, INP/LCP have different ceilings), and sent to Sentry as spans with measurements + structured logs with aggregated percentiles.

Key intervention points

Layer What changes Why
UI startup (ui/index.js) Calls initWebVitals() Observers must register early to capture first interactions
Sentry scope (web-vitals.ts) Tags, context, breadcrumbs — no setMeasurement Numeric measurements can't attach to transactions on extension pages; tags/context survive regardless
Benchmark flows (bridge, confirm-tx, load-new-account) collectWebVitals(driver) after user actions INP requires real interactions to report; collection happens at end of measurement window
Benchmark aggregation (statistics.ts, runner.ts) Per-metric bounds + standard statistical pipeline CLS range [0, 10] vs INP/LCP in milliseconds — can't reuse timer bounds directly
Sentry reporting (send-to-sentry.ts) Separate path: spans with measurements for per-run data, structured logs for aggregates Timer data uses structured logs only; web vitals need span-level measurements for Sentry's performance UI
PR comment (metamaskbot-build-announce) buildWebVitalsSection renders CWV table inside Performance Benchmarks Dedicated table with p75, selective p95, rating distributions — separate from timer tables due to different units/semantics
Browserify build (scripts.js) web-vitals added to Babelify only list Package has "type": "module" — Browserify resolves to the ESM stub and needs Babelify to transpile it to CJS (same pattern as lightweight-charts, firebase, etc.)

What this unlocks

Question Before After
Is the extension janky for users? Unknown Filter by inp.rating:poor on existing transactions
Did this release regress responsiveness? Unknown Compare INP p75/p95 across benchmark runs
Which interactions cause worst INP? Unknown Attribution context: inp_attribution.interactionTarget
Are errors correlated with perf problems? Unknown Breadcrumbs show "INP: 450ms (poor)" before errors
Visual stability in CI? Unknown CLS scores with layout shift attribution per benchmark

Platform constraints (documented in source)

Chrome's chrome-extension:// protocol does not emit PerformancePaintTiming or largest-contentful-paint entries. LCP and CLS observers will not fire in production — they exist for CI benchmark coverage only. INP (PerformanceEventTiming) is the only metric expected to report in production. This is documented in the module-level JSDoc and per-observer notes.

Output examples

1. PR comment — Core Web Vitals table

Rendered inside the Performance Benchmarks collapsible, between Interaction Benchmarks and Startup Benchmarks. Uses buildWebVitalsSection from development/metamaskbot-build-announce/utils.ts.

📊 Core Web Vitals
FlowMetricp75p95Rating (G/NI/P)Samples
Confirm TxINP128ms192ms4/1/05
LCP1.8s2.1s5/0/05
CLS0.012-5/0/05
Bridge User ActionsINP96ms144ms5/0/05
LCP1.2s1.5s5/0/05

Column semantics:

  • p75 — typical experience (Google's Core Web Vitals threshold)
  • p95 — tail latency. Shown for INP (meaningful: captures worst interaction jank) and LCP; omitted (-) for CLS (unitless, distribution is more informative)
  • Rating (G/NI/P) — count of runs rated Good / Needs Improvement / Poor per Google's thresholds: INP <200ms/500ms, LCP <2.5s/4s, CLS <0.1/0.25
  • Samples — number of runs that reported a value (excludes nulls from runs where the observer didn't fire)

Metrics with no data across all flows (all nulls) are omitted entirely — the section won't render if no web vitals were captured.

2. Benchmark JSON artifact

Each interaction benchmark preset produces a JSON artifact (e.g. benchmark-chrome-browserify-interactionUserActions.json) containing webVitals alongside timer statistics. The webVitals field preserves per-run snapshots for Sentry spans and includes aggregated statistics:

{
  "confirmTx": {
    "testTitle": "benchmark-user-actions-confirm-tx",
    "persona": "standard",
    "benchmarkType": "userAction",
    "mean": { "confirm_tx": 6046, "total": 6046 },
    "p75": { "confirm_tx": 6055, "total": 6055 },
    "p95": { "confirm_tx": 6055, "total": 6055 },
    "webVitals": {
      "runs": [
        { "inp": 120, "inpRating": "good", "lcp": 1800, "lcpRating": "good", "cls": 0.01, "clsRating": "good", "iteration": 0 },
        { "inp": 135, "inpRating": "good", "lcp": 1950, "lcpRating": "good", "cls": 0.008, "clsRating": "good", "iteration": 1 },
        { "inp": 190, "inpRating": "good", "lcp": 2100, "lcpRating": "good", "cls": null, "clsRating": null, "iteration": 2 },
        { "inp": 128, "inpRating": "good", "lcp": 1750, "lcpRating": "good", "cls": 0.012, "clsRating": "good", "iteration": 3 },
        { "inp": 245, "inpRating": "needs-improvement", "lcp": 1820, "lcpRating": "good", "cls": 0.009, "clsRating": "good", "iteration": 4 }
      ],
      "aggregated": {
        "inp": {
          "id": "inp", "mean": 163.6, "min": 120, "max": 245, "stdDev": 50.2,
          "cv": 0.307, "p50": 135, "p75": 192, "p95": 245, "p99": 245,
          "samples": 5, "outliers": 0, "dataQuality": "good"
        },
        "lcp": {
          "id": "lcp", "mean": 1884, "min": 1750, "max": 2100, "stdDev": 135.3,
          "cv": 0.072, "p50": 1820, "p75": 1950, "p95": 2100, "p99": 2100,
          "samples": 5, "outliers": 0, "dataQuality": "good"
        },
        "cls": {
          "id": "cls", "mean": 0.0098, "min": 0.008, "max": 0.012, "stdDev": 0.0016,
          "cv": 0.167, "p50": 0.009, "p75": 0.012, "p95": 0.012, "p99": 0.012,
          "samples": 4, "outliers": 0, "dataQuality": "good"
        },
        "ratings": {
          "inp": { "good": 4, "needs-improvement": 1, "poor": 0, "null": 0 },
          "lcp": { "good": 5, "needs-improvement": 0, "poor": 0, "null": 0 },
          "cls": { "good": 4, "needs-improvement": 0, "poor": 0, "null": 1 }
        }
      }
    }
  }
}

Note: aggregated statistics use the same TimerStatistics type as timer data (mean, stdDev, p50–p99, outlier count, data quality) but with metric-specific sanity bounds — CLS capped at [0, 10], INP/LCP in milliseconds with higher ceilings.

3. Sentry CI reporting (send-to-sentry.ts)

Web vitals are reported to Sentry via two separate paths, distinct from timer data:

Per-run spans — each benchmark iteration becomes a span with measurements:

span: benchmark.confirmTx.webVitals
  op: benchmark.webvitals
  attributes:
    ci.branch: "jongsun/perf/260202-web-vitals"
    ci.prNumber: "39716"
    ci.commitHash: "abc1234"
    ci.browser: "chrome"
    ci.buildType: "browserify"
    ci.persona: "standard"
    ci.testTitle: "benchmark-user-actions-confirm-tx"
    webVitals.iteration: 0
    benchmark.inp: 120
    inp.rating: "good"
    benchmark.lcp: 1800
    lcp.rating: "good"
    benchmark.cls: 0.01
    cls.rating: "good"
  measurements:
    benchmark.inp: 120 (millisecond)
    benchmark.lcp: 1800 (millisecond)
    benchmark.cls: 0.01 (none)

Aggregated structured log — one per benchmark with summary statistics:

logger.info: benchmark.confirmTx.webVitals.summary
  attributes:
    ci.branch: "jongsun/perf/260202-web-vitals"
    ci.persona: "standard"
    webVitals.runs: 5
    webVitals.inp.mean: 163.6
    webVitals.inp.p75: 192
    webVitals.inp.p95: 245
    webVitals.inp.samples: 5
    webVitals.inp.dataQuality: "good"
    webVitals.inp.ratings.good: 4
    webVitals.inp.ratings.needsImprovement: 1
    webVitals.inp.ratings.poor: 0
    webVitals.lcp.mean: 1884
    webVitals.lcp.p75: 1950
    webVitals.lcp.p95: 2100
    webVitals.lcp.samples: 5
    webVitals.lcp.dataQuality: "good"
    webVitals.lcp.ratings.good: 5
    ...

4. Production Sentry enrichment (web-vitals.ts)

In production, only INP fires on chrome-extension:// pages. When it does, the Sentry scope is enriched with no additional transactions or events — data piggybacks on the next error:

Tag (filterable in Sentry Discover):

inp.rating: "poor"

Context (attached to error events):

inp_attribution: {
  interactionTarget: "#confirm-approve-button",
  interactionType: "pointer",
  loadState: "complete",
  inputDelay: 12,
  processingDuration: 380,
  presentationDelay: 58
}

Breadcrumb (visible in error timeline, only for poor/needs-improvement):

category: performance.inp
message: "INP: 450ms (poor)"
level: warning
data: { interactionTarget: "#confirm-approve-button", ... }

Relation to other work

  • Long Task / TBT instrumentation (#39715) — Companion PR adding PerformanceObserver('longtask') for Total Blocking Time. Together, INP (symptom) + TBT (cause) provide a complete picture of main thread responsiveness.
  • Benchmark pipeline — Extends the existing timer-based benchmark infrastructure (same statistical pipeline, same Sentry reporting, same CI workflow) rather than introducing a parallel system.
  • Epic: MetaMask/MetaMask-planning#6741

The @ts-expect-error suppress CommonJS vs ECMAScript error on the web-vitals/attribution import follows the codebase-wide convention used for chart.js, react-chartjs-2, and lightweight-charts (TS1479 — the repo is CJS but the package declares "type": "module"; Webpack resolves via the exports default condition, Browserify falls back to the ESM stub which Babelify transpiles to CJS).

Open in GitHub Codespaces

Changelog

CHANGELOG entry: null

Related issues

Fixes: MetaMask/MetaMask-planning#6735
Fixes: MetaMask/MetaMask-planning#6736
Fixes: MetaMask/MetaMask-planning#6739
Related epic: MetaMask/MetaMask-planning#6741

Manual testing steps

  1. Run unit tests: yarn test:unit ui/helpers/utils/web-vitals.test.ts
  2. Start a dev build: yarn start
  3. Open the extension popup — verify no console errors from web-vitals initialization
  4. Interact with the UI (click buttons, navigate pages) — INP observer should capture interactions
  5. To verify benchmark integration:
    • Build test build: yarn build:test
    • Run a benchmark flow: yarn test:e2e:benchmark (or a specific user-action benchmark)
    • Check that webVitals appears in the JSON benchmark output alongside timers

Screenshots/Recordings

Before

N/A — no UI changes. This is instrumentation-only.

After

N/A — no UI changes. This is instrumentation-only.

Pre-merge author checklist

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

Note

Medium Risk
Touches UI startup initialization and CI telemetry/reporting paths; issues would mainly affect performance instrumentation/CI outputs rather than core wallet behavior, but Sentry noise/overhead or observer errors could impact startup if misbehaving.

Overview
Adds Core Web Vitals (INP/LCP/CLS) instrumentation via the new web-vitals dependency, initializing observers on UI startup and exposing getters/resetters through stateHooks for test runs.

Extends the E2E benchmark pipeline to collect per-iteration web vitals from interaction flows, aggregate them with metric-specific sanity bounds/outlier handling, and include the summaries in benchmark JSON output.

Updates CI reporting and PR feedback: send-to-sentry.ts now emits per-run web vitals as Sentry spans (plus an aggregated structured log), and the build announce bot renders a new “Core Web Vitals” table section; build/LavaMoat policies are adjusted to allow/transpile the ESM-only web-vitals package.

Written by Cursor Bugbot for commit 55ba7e2. This will update automatically on new commits. Configure here.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 2, 2026

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@MajorLift MajorLift force-pushed the jongsun/perf/260202-web-vitals branch from da2ee35 to d1198dd Compare February 3, 2026 00:06
@github-actions github-actions bot added size-L and removed size-M labels Feb 3, 2026
@socket-security
Copy link

socket-security bot commented Feb 3, 2026

All alerts resolved. Learn more about Socket for GitHub.

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

Ignoring alerts on:

  • web-vitals@4.2.4

View full report

@MajorLift MajorLift force-pushed the jongsun/perf/260202-web-vitals branch from d1198dd to 62ccd2a Compare February 3, 2026 00:13
@MajorLift MajorLift marked this pull request as draft February 3, 2026 00:13
@MajorLift
Copy link
Contributor Author

@metamaskbot update-policies

@metamaskbot
Copy link
Collaborator

Policy update failed. You can review the logs or retry the policy update here

@MajorLift MajorLift force-pushed the jongsun/perf/260202-web-vitals branch from 62ccd2a to 58ba0e0 Compare February 6, 2026 15:46
@github-actions github-actions bot added size-XL and removed size-L labels Feb 6, 2026
@MajorLift MajorLift force-pushed the jongsun/perf/260202-web-vitals branch from 58ba0e0 to ef3b66c Compare February 6, 2026 16:28
@MajorLift MajorLift force-pushed the jongsun/perf/260202-web-vitals branch from ef3b66c to 379a3a0 Compare February 6, 2026 16:31
…ting

Implements INP, LCP, and CLS observers using the `web-vitals-attribution`
build. Each observer:
- Stores latest metric value + rating in module-level state
- Reports to Sentry via `globalThis.sentry` (`setMeasurement`, `setTag`,
  `setContext` for attribution, breadcrumb for poor/needs-improvement)
- Computes ratings using Google's thresholds:
    INP: good <200ms, poor >500ms
    LCP: good <2500ms, poor >4000ms
    CLS: good <0.1, poor >0.25

Exposes `getWebVitalsMetrics()` and `resetWebVitalsMetrics()` for
E2E benchmark retrieval via `stateHooks`.

Naming follows `benchmark.{metric}` / `{metric}.rating` convention
for Sentry dashboard compatibility.

Add Core Web Vitals instrumentation with attribution and Sentry reporting

Implements INP, LCP, and CLS observers using the `web-vitals-attribution`
build. Each observer:
- Stores latest metric value + rating in module-level state
- Reports to Sentry via `globalThis.sentry` (`setMeasurement`, `setTag`,
  `setContext` for attribution, breadcrumb for poor/needs-improvement)
- Computes ratings using Google's thresholds:
    INP: good <200ms, poor >500ms
    LCP: good <2500ms, poor >4000ms
    CLS: good <0.1, poor >0.25

Exposes `getWebVitalsMetrics()` and `resetWebVitalsMetrics()` for
E2E benchmark retrieval via `stateHooks`.

Naming follows `benchmark.{metric}` / `{metric}.rating` convention
for Sentry dashboard compatibility.
Covers all three observers (INP, LCP, CLS) and shared infrastructure:
- Observer registration via `onINP`/`onLCP`/`onCLS` callbacks
- Sentry reporting: `setMeasurement`, `setTag`, `setContext`, breadcrumbs
- Rating thresholds: good, needs-improvement, poor for each metric
- Metric storage: `getWebVitalsMetrics()` returns copy (no mutation leak)
- `resetWebVitalsMetrics()` clears all values to null
- Graceful degradation when `globalThis.sentry` is undefined
Calls `initWebVitals()` at the top of `startApp()`, before trace setup.
This starts INP, LCP, and CLS observers as early as possible so LCP
captures the initial render and CLS tracks shifts from first paint.

INP only reports meaningful data after user interactions, so early
init has no downside — it just ensures the observer is ready.
…peline

Core measurement type mirroring `ui/helpers/utils/web-vitals.ts`.
All fields are nullable — INP is null before any user interaction,
LCP may be null on non-initial loads, CLS may be null if no layout
shifts occurred.

TODO: consolidate with the duplicate `WebVitalsMetrics` type in
`ui/helpers/utils/web-vitals.ts` to a single shared definition.
`RatingDistribution`: categorical count of good/needs-improvement/poor/null
across benchmark runs. Enables quality gate checks like "80% of runs
rated 'good' for INP".

`WebVitalsAggregated`: per-metric `TimerStatistics` (mean, percentiles,
outlier counts) plus rating distributions. Reuses the existing
`TimerStatistics` shape — CLS values work here because the upstream
`calculateWebVitalsStatistics` uses metric-specific bounds.

`WebVitalsSummary`: holds both per-run snapshots (for Sentry spans) and
aggregated stats (for dashboards). Dual representation preserves
granularity while providing quick summary.
@metamaskbotv2
Copy link
Contributor

metamaskbotv2 bot commented Feb 17, 2026

Builds ready [80dec5a]
UI Startup Metrics (1376 ± 96 ms)
PlatformBuildTypePageMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyStandard HomeuiStartup1376119017499614071567
load1190101815309112291365
domContentLoaded1184100815209012221357
domInteractive2817105182474
firstPaint170651260166214312
backgroundConnect25423131615261282
firstReactRender17103751927
initialActions107113
loadScripts9457721275899861111
setupStore1363351720
numNetworkReqs231589201581
BrowserifyPower User HomeuiStartup17291388209813817712003
load11721049176515711491596
domContentLoaded11571042171814811381528
domInteractive3419200253383
firstPaint180731683171216298
backgroundConnect33529740624352379
firstReactRender23154572540
initialActions102112
loadScripts89979214241458821274
setupStore1684771829
numNetworkReqs1144524947138220
WebpackStandard HomeuiStartup88371111481089791055
load7716381047102857939
domContentLoaded7656331041101849932
domInteractive2917100192578
firstPaint1216640665142249
backgroundConnect27196283241
firstReactRender14103141622
initialActions103112
loadScripts7626311039100842929
setupStore1363751325
numNetworkReqs231588201583
WebpackPower User HomeuiStartup1289941189317313671641
load75865512411327441119
domContentLoaded74864812311327351107
domInteractive42211973540121
firstPaint1467047372171317
backgroundConnect17713536648193276
firstReactRender24174142533
initialActions103111
loadScripts74564612171297331100
setupStore1365061519
numNetworkReqs1174426251146231
FirefoxBrowserifyStandard HomeuiStartup15771346228018215892036
load13691167183712814051663
domContentLoaded13681166183312814021662
domInteractive803224654100204
firstPaint------
backgroundConnect58282383156134
firstReactRender1292521213
initialActions102012
loadScripts13421141168712013841626
setupStore176207311337
numNetworkReqs2412100221886
BrowserifyPower User HomeuiStartup27732132455738428463618
load15871285245125616002208
domContentLoaded15861285245125616002208
domInteractive12236718121111414
firstPaint------
backgroundConnect33792997287297943
firstReactRender20156462127
initialActions208122
loadScripts15501263239424515492121
setupStore1348722178149554
numNetworkReqs66351473390134
WebpackStandard HomeuiStartup16261371221616716871944
load14041217171911314621600
domContentLoaded14031212171911314621600
domInteractive772923848121144
firstPaint------
backgroundConnect57272243360130
firstReactRender14112931421
initialActions102012
loadScripts13761202168710814371571
setupStore206207351361
numNetworkReqs241291201781
WebpackPower User HomeuiStartup27681991436149529073822
load15991342263728117652167
domContentLoaded15991338263728117652167
domInteractive13733813165115612
firstPaint------
backgroundConnect2801221313229271847
firstReactRender23167082632
initialActions203122
loadScripts15611308258326717292078
setupStore177111143239225715
numNetworkReqs66341403498131
📊 Page Load Benchmark Results

Current Commit: 80dec5a | Date: 2/17/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.09s (±76ms) 🟡 | historical mean value: 1.04s ⬆️ (historical data)
  • domContentLoaded-> current mean value: 774ms (±74ms) 🟢 | historical mean value: 730ms ⬆️ (historical data)
  • firstContentfulPaint-> current mean value: 85ms (±12ms) 🟢 | historical mean value: 83ms ⬆️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.09s 76ms 1.05s 1.38s 1.33s 1.38s
domContentLoaded 774ms 74ms 734ms 1.06s 999ms 1.06s
firstPaint 85ms 12ms 72ms 188ms 96ms 188ms
firstContentfulPaint 85ms 12ms 72ms 188ms 96ms 188ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 633 Bytes (0.01%)
  • ui: 49.47 KiB (0.6%)
  • common: 4.3 KiB (0.04%)

@metamaskbotv2
Copy link
Contributor

metamaskbotv2 bot commented Feb 18, 2026

Builds ready [8be1199]
UI Startup Metrics (1422 ± 107 ms)
PlatformBuildTypePageMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyStandard HomeuiStartup14221219194110714721596
load1217102616509912501399
domContentLoaded1209102416419612451377
domInteractive2816109192480
firstPaint167701646165207304
backgroundConnect25623339320263286
firstReactRender18123331823
initialActions104113
loadScripts97179013989510111126
setupStore1474061624
numNetworkReqs312292202284
BrowserifyPower User HomeuiStartup2951141710930202334675507
load12941080236218713311698
domContentLoaded12721068233918013081627
domInteractive39192173138112
firstPaint227881645229262394
backgroundConnect1098302845017137042962
firstReactRender24154662536
initialActions103112
loadScripts1007832194816710331352
setupStore1676991832
numNetworkReqs1154922640144186
WebpackStandard HomeuiStartup89370713681109411068
load773627107497837929
domContentLoaded767622106696831922
domInteractive3016110222689
firstPaint1256577290135251
backgroundConnect2819116123043
firstReactRender1813117111726
initialActions104112
loadScripts764620106395829915
setupStore1162531217
numNetworkReqs312293202586
WebpackPower User HomeuiStartup1331924270529313631814
load77168111491007781001
domContentLoaded7596681143101766994
domInteractive41221432340102
firstPaint1618435267209283
backgroundConnect192137947114170391
firstReactRender23183332529
initialActions101011
loadScripts7566661134100764985
setupStore1352451721
numNetworkReqs1404728238152213
FirefoxBrowserifyStandard HomeuiStartup16731440255524316762343
load14111210221120714161973
domContentLoaded14101204221120714151973
domInteractive763121844101154
firstPaint------
backgroundConnect5828178205882
firstReactRender14112121416
initialActions102012
loadScripts13841187218620313921875
setupStore156136141533
numNetworkReqs311995192782
BrowserifyPower User HomeuiStartup27892089769661029413488
load15841295622152115851976
domContentLoaded15841294622152115841976
domInteractive171441749195198394
firstPaint------
backgroundConnect3501191225248418891
firstReactRender19137191829
initialActions102122
loadScripts15411258610151015411837
setupStore12711796171113623
numNetworkReqs913819838106176
WebpackStandard HomeuiStartup17751474318724617932178
load14801248274217215131722
domContentLoaded14791248274117215131722
domInteractive953027148132167
firstPaint------
backgroundConnect65283844268118
firstReactRender16132941727
initialActions1015122
loadScripts14541225270116814851692
setupStore257191321985
numNetworkReqs311990172777
WebpackPower User HomeuiStartup30442029933493431933911
load16881320355239117312424
domContentLoaded16881319355239117312424
domInteractive196501556224186673
firstPaint------
backgroundConnect60112864307937231102
firstReactRender23156262532
initialActions204123
loadScripts16371300349037616902298
setupStore16320852197206584
numNetworkReqs92392505191228
📊 Page Load Benchmark Results

Current Commit: 8be1199 | Date: 2/18/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.05s (±72ms) 🟡 | historical mean value: 1.05s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 740ms (±69ms) 🟢 | historical mean value: 745ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 80ms (±13ms) 🟢 | historical mean value: 86ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.05s 72ms 1.01s 1.33s 1.27s 1.33s
domContentLoaded 740ms 69ms 700ms 1.01s 946ms 1.01s
firstPaint 80ms 13ms 64ms 188ms 96ms 188ms
firstContentfulPaint 80ms 13ms 64ms 188ms 96ms 188ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 58 Bytes (0%)
  • ui: 15.54 KiB (0.19%)
  • common: 42 Bytes (0%)

@metamaskbotv2
Copy link
Contributor

metamaskbotv2 bot commented Feb 23, 2026

Builds ready [bfd0057]
⚡ Performance Benchmarks (1386 ± 118 ms)
👆 Interaction Benchmarks
ActionMetricMean (ms)Std Dev (ms)P75 (ms)P95 (ms)
Load New Accountload_new_account3547364364
total3547364364
Confirm Txconfirm_tx6046760556055
total6046760556055
Bridge User Actionsbridge_load_page20813213225
bridge_load_asset_picker17422173210
bridge_search_token71018722739
total11376912191224
🔌 Startup Benchmarks
BuildMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
Chrome Browserify Startup Standard HomeuiStartup13861150175411814321623
load1174976150310612211392
domContentLoaded1167971149810512171386
domInteractive281599192477
firstPaint1406536871194266
backgroundConnect20618325615210242
firstReactRender17133031922
initialActions105112
loadScripts978791129410310141179
setupStore1263341520
numNetworkReqs312290192585
Chrome Browserify Startup Power User HomeuiStartup18931355462447619202639
load11851044175614212121525
domContentLoaded11661032174913612041464
domInteractive39211712639105
firstPaint1896843283251328
backgroundConnect3582523054299319600
firstReactRender23164572537
initialActions102112
loadScripts96083515251329801264
setupStore19863112145
numNetworkReqs66381472375122
Chrome Webpack Startup Standard HomeuiStartup85468912011029011045
load72260891786761876
domContentLoaded71760390485757872
domInteractive2915128242491
firstPaint1155965373132211
backgroundConnect26184963037
firstReactRender18124272133
initialActions105112
loadScripts71460289784755870
setupStore1264561220
numNetworkReqs312296212589
Chrome Webpack Startup Power User HomeuiStartup1220996199318313121555
load7296241236113729992
domContentLoaded7196201229113717983
domInteractive38181462935120
firstPaint1386246571158262
backgroundConnect17612953167168316
firstReactRender23175752529
initialActions103111
loadScripts7176171220111715973
setupStore1483651518
numNetworkReqs68351593177147
Firefox Browserify Startup Standard HomeuiStartup17241525268521217192216
load14511278233417514621744
domContentLoaded14501278233417514621744
domInteractive11334901100137273
firstPaint------
backgroundConnect64332113164144
firstReactRender14111711415
initialActions102122
loadScripts14231253230317214361714
setupStore198188221653
numNetworkReqs312091182780
Firefox Browserify Startup Power User HomeuiStartup28342147806866829613618
load16401295678159416692251
domContentLoaded16401295678059416692251
domInteractive154341428192118552
firstPaint------
backgroundConnect287110961223325877
firstReactRender191386111922
initialActions203122
loadScripts15931275674958616202176
setupStore118977817589556
numNetworkReqs70271683689141
Firefox Webpack Startup Standard HomeuiStartup17491472314824517812096
load14591232273815814901637
domContentLoaded14591232273815814891637
domInteractive823021439107145
firstPaint------
backgroundConnect65252474366158
firstReactRender17126161724
initialActions103122
loadScripts14331217271615514661560
setupStore237231331668
numNetworkReqs312094172776
Firefox Webpack Startup Power User HomeuiStartup27011960441146528513699
load15681257305132217282268
domContentLoaded15681257305132317262268
domInteractive14131733153108537
firstPaint------
backgroundConnect3101161019251328912
firstReactRender21156862328
initialActions203122
loadScripts15351245300931616822204
setupStore19171178235289666
numNetworkReqs68291863587136
🧭 User Journey Benchmarks
BenchmarkMetricMean (ms)Std Dev (ms)P75 (ms)P95 (ms)
Onboarding Import WalletimportWalletToSocialScreen2171217218
srpButtonToSrpForm8908990
confirmSrpToPwForm2102121
pwFormToMetricsScreen1501515
metricsToWalletReadyScreen1501616
doneButtonToHomeScreen104723312051409
openAccountMenuToAccountListLoaded711833072027623
total833739687648864
Onboarding New WalletcreateWalletToSocialScreen2192220222
srpButtonToPwForm1031103105
createPwToRecoveryScreen8088
skipBackupToMetricsScreen3413536
agreeButtonToOnboardingSuccess1611617
doneButtonToAssetList95834113131411
total134134116951791
Asset DetailsassetClickToPriceChart46145571
total46145571
Solana Asset DetailsassetClickToPriceChart4614748
total4614748
Import Srp HomeloginToHomeScreen19243519561962
openAccountMenuAfterLogin4214343
homeAfterImportWithNewWallet255412725182770
total451911745214708
Send TransactionsopenSendPageFromHome34144153
selectTokenToSendFormLoaded3093645
reviewTransactionToConfirmationPage8672870870
total93222952956
SwapopenSwapPageFromHome1195123124
fetchAndDisplaySwapQuotes46366246164739
total47546547364860
🌐 Dapp Page Load Benchmarks

Current Commit: bfd0057 | Date: 2/23/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.05s (±74ms) 🟡 | historical mean value: 1.04s ⬆️ (historical data)
  • domContentLoaded-> current mean value: 734ms (±69ms) 🟢 | historical mean value: 727ms ⬆️ (historical data)
  • firstContentfulPaint-> current mean value: 76ms (±11ms) 🟢 | historical mean value: 80ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.05s 74ms 1.01s 1.34s 1.26s 1.34s
domContentLoaded 734ms 69ms 700ms 1.01s 941ms 1.01s
firstPaint 76ms 11ms 56ms 160ms 88ms 160ms
firstContentfulPaint 76ms 11ms 56ms 160ms 88ms 160ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 58 Bytes (0%)
  • ui: 15.54 KiB (0.19%)
  • common: 42 Bytes (0%)

… persona constant

PR comment now renders a Core Web Vitals table under Performance Benchmarks
(between Interaction Benchmarks and Startup Benchmarks):
- INP: p75 + p95 (both meaningful for interaction latency)
- LCP: p75 only (p95 is noise with small CI samples), marked CI-only
- CLS: p75 only (unitless, rating distribution more informative than percentiles)
- Rating distribution column (good/NI/poor) for quick regression signal

`fetchAndBuildWebVitalsSection` reads from interaction preset artifacts
which are the only benchmarks collecting web vitals via `collectWebVitals`.

Also replaces hardcoded `'standard'` with `BENCHMARK_PERSONA.STANDARD`
in `send-to-sentry.ts` for consistency with adjacent usage.
@metamaskbotv2
Copy link
Contributor

metamaskbotv2 bot commented Feb 23, 2026

Builds ready [6b62a8c]
⚡ Performance Benchmarks (1365 ± 107 ms)
👆 Interaction Benchmarks
ActionMetricMean (ms)Std Dev (ms)P75 (ms)P95 (ms)
Load New Accountload_new_account2798276293
total2798276293
Confirm Txconfirm_tx61103461236169
total61103461236169
Bridge User Actionsbridge_load_page28817299308
bridge_load_asset_picker24043268299
bridge_search_token7421742743
total1257812661266
🔌 Startup Benchmarks
BuildMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
Chrome Browserify Startup Standard HomeuiStartup13651131186710713971575
load114994715469311831334
domContentLoaded114194215389011701326
domInteractive2616103192378
firstPaint194651544232211312
backgroundConnect19818126214199221
firstReactRender17122531923
initialActions105113
loadScripts9587591328899921138
setupStore1262741520
numNetworkReqs312288192282
Chrome Browserify Startup Power User HomeuiStartup2622147010629173524304872
load12141037198315512521524
domContentLoaded11961025195115112301504
domInteractive38201832936104
firstPaint230841306178274431
backgroundConnect950262883014595523080
firstReactRender2615170162640
initialActions105113
loadScripts990832172514710241281
setupStore1684971833
numNetworkReqs75401472388120
Chrome Webpack Startup Standard HomeuiStartup8877031140999491072
load75861195893839896
domContentLoaded75260694992833888
domInteractive2715101192376
firstPaint1176436967152260
backgroundConnect271883103044
firstReactRender17114261929
initialActions104112
loadScripts74960494091829885
setupStore1274251324
numNetworkReqs312297202586
Chrome Webpack Startup Power User HomeuiStartup1269878223620713851629
load75963710951047671025
domContentLoaded74863210881057541018
domInteractive40201782839116
firstPaint1536950085179345
backgroundConnect18813256476180309
firstReactRender23183432430
initialActions104111
loadScripts74563010791037521008
setupStore1454661621
numNetworkReqs1244127243146190
Firefox Browserify Startup Standard HomeuiStartup16331399240718116551977
load13831194207614414211652
domContentLoaded13821194207614414211652
domInteractive953129657134194
firstPaint------
backgroundConnect57272172957119
firstReactRender13111911315
initialActions102012
loadScripts13561171204113413991628
setupStore167139181631
numNetworkReqs312099212590
Firefox Browserify Startup Power User HomeuiStartup314420629437139030254032
load190013327663125616932883
domContentLoaded189913277662125616932883
domInteractive160531461170155413
firstPaint------
backgroundConnect38612213563014581203
firstReactRender221571122047
initialActions203122
loadScripts185213087403124016612854
setupStore148121097205123632
numNetworkReqs79281874198174
Firefox Webpack Startup Standard HomeuiStartup16651393240415916991992
load14011197182710614551603
domContentLoaded14001197182610614551603
domInteractive942826153132184
firstPaint------
backgroundConnect59242333664130
firstReactRender15123231519
initialActions102012
loadScripts13771175173410014341578
setupStore185147221558
numNetworkReqs312097182778
Firefox Webpack Startup Power User HomeuiStartup27651974978079628693367
load16291309767867816182205
domContentLoaded16291309767767816172204
domInteractive184491377204197595
firstPaint------
backgroundConnect3601201129270454938
firstReactRender20156062229
initialActions207122
loadScripts15941278762467315862056
setupStore1489802209136728
numNetworkReqs77212434780177
🧭 User Journey Benchmarks
BenchmarkMetricMean (ms)Std Dev (ms)P75 (ms)P95 (ms)
Onboarding Import WalletimportWalletToSocialScreen2221223223
srpButtonToSrpForm984100104
confirmSrpToPwForm2312425
pwFormToMetricsScreen1601717
metricsToWalletReadyScreen1922022
doneButtonToHomeScreen106520212291253
openAccountMenuToAccountListLoaded741030076877843
total88513588878887
Onboarding New WalletcreateWalletToSocialScreen2223224225
srpButtonToPwForm1145120120
createPwToRecoveryScreen911010
skipBackupToMetricsScreen4013942
agreeButtonToOnboardingSuccess1811819
doneButtonToAssetList90034912961349
total130635116881771
Asset DetailsassetClickToPriceChart50187073
total50187073
Solana Asset DetailsassetClickToPriceChart4735151
total4735151
Import Srp HomeloginToHomeScreen212418722222440
openAccountMenuAfterLogin4745051
homeAfterImportWithNewWallet24737324372599
total456518345304871
Send TransactionsopenSendPageFromHome29114143
selectTokenToSendFormLoaded29123848
reviewTransactionToConfirmationPage8542856856
total9207923930
SwapopenSwapPageFromHome8832116116
fetchAndDisplaySwapQuotes529888763756392
total538687864136508
🌐 Dapp Page Load Benchmarks

Current Commit: 6b62a8c | Date: 2/23/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.07s (±72ms) 🟡 | historical mean value: 1.04s ⬆️ (historical data)
  • domContentLoaded-> current mean value: 749ms (±70ms) 🟢 | historical mean value: 728ms ⬆️ (historical data)
  • firstContentfulPaint-> current mean value: 80ms (±14ms) 🟢 | historical mean value: 80ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.07s 72ms 1.00s 1.36s 1.29s 1.36s
domContentLoaded 749ms 70ms 694ms 1.04s 955ms 1.04s
firstPaint 80ms 14ms 64ms 204ms 88ms 204ms
firstContentfulPaint 80ms 14ms 64ms 204ms 88ms 204ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: -204 Bytes (0%)
  • ui: 15.53 KiB (0.19%)
  • common: 3.08 KiB (0.03%)

… persona constant

PR comment now renders a Core Web Vitals table under Performance Benchmarks
(between Interaction Benchmarks and Startup Benchmarks):
- INP: p75 + p95 (both meaningful for interaction latency)
- LCP: p75 only (p95 is noise with small CI samples), marked CI-only
- CLS: p75 only (unitless, rating distribution more informative than percentiles)
- Rating distribution column (good/NI/poor) for quick regression signal

`fetchAndBuildWebVitalsSection` reads from interaction preset artifacts
which are the only benchmarks collecting web vitals via `collectWebVitals`.

Also replaces hardcoded `'standard'` with `BENCHMARK_PERSONA.STANDARD`
in `send-to-sentry.ts` for consistency with adjacent usage.
@MajorLift MajorLift marked this pull request as ready for review February 23, 2026 16:37
…ting

- `getRating` in `web-vitals.ts`: change `<` to `<=` to match web-vitals
  library behavior — value at threshold gets the better rating (e.g.
  200ms INP is "good", not "needs-improvement")
- Remove unused `createEmptyWebVitalsMetrics` from `web-vitals-collector.ts`
  and its barrel export
- Remove `Sentry.setMeasurement` from `send-to-sentry.ts` — in Sentry v10
  `setMeasurement` attaches to the root transaction, not the child span
  created by `startSpan`; values already captured via `span.setAttribute`
- Simplify `WEB_VITALS_BOUNDS` in `statistics.ts`: replace `allowZero`
  flag with uniform `value < bounds.min` check — min=1 for INP/LCP
  now correctly includes 1ms (was excluded by `<=`), min=0 for CLS
  still allows 0 (perfect stability)
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

@metamaskbotv2
Copy link
Contributor

metamaskbotv2 bot commented Feb 23, 2026

Builds ready [5097afc]
⚡ Performance Benchmarks (1361 ± 99 ms)
👆 Interaction Benchmarks
ActionMetricMean (ms)Std Dev (ms)P75 (ms)P95 (ms)
Load New Accountload_new_account32142347374
total32142347374
Confirm Txconfirm_tx60682860906100
total60682860906100
Bridge User Actionsbridge_load_page23327264266
bridge_load_asset_picker19436210241
bridge_search_token72013734737
total11492311481186
🔌 Startup Benchmarks
BuildMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
Chrome Browserify Startup Standard HomeuiStartup1361115716739913821557
load114497014058811691316
domContentLoaded113796513818611651307
domInteractive2717104202387
firstPaint182651137185206365
backgroundConnect19918724612201224
firstReactRender18133641926
initialActions107113
loadScripts9547811207879841127
setupStore1372841519
numNetworkReqs312296202286
Chrome Browserify Startup Power User HomeuiStartup2897146710839179339766166
load12201058183815512551593
domContentLoaded12011036175714512331527
domInteractive3720145253797
firstPaint235741749278260392
backgroundConnect11102637876137119192983
firstReactRender24154462636
initialActions106112
loadScripts992841154014110231318
setupStore1775181931
numNetworkReqs79401412396118
Chrome Webpack Startup Standard HomeuiStartup8616941230999071089
load740618114995793894
domContentLoaded733615114294789889
domInteractive2716116212381
firstPaint1116140160124213
backgroundConnect29198293242
firstReactRender18114252028
initialActions105113
loadScripts731613114093787879
setupStore1354161326
numNetworkReqs312297212588
Chrome Webpack Startup Power User HomeuiStartup1209913309525512531533
load7186141231102726951
domContentLoaded7086091220103712942
domInteractive3618134213779
firstPaint15766956110180297
backgroundConnect16712934944158286
firstReactRender22173432428
initialActions104111
loadScripts7056071212101709940
setupStore1355271420
numNetworkReqs1253925240148174
Firefox Browserify Startup Standard HomeuiStartup17271497278421417262111
load14541267236017814711730
domContentLoaded14531267236017814691730
domInteractive943536956128149
firstPaint------
backgroundConnect62282382966104
firstReactRender14122011516
initialActions103122
loadScripts14251243225916914441627
setupStore207257311646
numNetworkReqs312095192786
Firefox Browserify Startup Power User HomeuiStartup29342034873393930073433
load17091325729487116762050
domContentLoaded17091324729487116752049
domInteractive148441053134142423
firstPaint------
backgroundConnect3581071483284440913
firstReactRender201477121959
initialActions203122
loadScripts16651304725987015972018
setupStore185101183207270595
numNetworkReqs82301953998174
Firefox Webpack Startup Standard HomeuiStartup17861466322431617902292
load14801213288121714951663
domContentLoaded14791213288121714951663
domInteractive963029548131149
firstPaint------
backgroundConnect70273955770168
firstReactRender16136161724
initialActions102122
loadScripts14541197285121314711623
setupStore36614221421574
numNetworkReqs312088182780
Firefox Webpack Startup Power User HomeuiStartup28311953856088728783432
load16761329734379516462120
domContentLoaded16761328734379516462119
domInteractive179451045187166650
firstPaint------
backgroundConnect3721251179275456973
firstReactRender20163642129
initialActions2147522
loadScripts16381310728279015662083
setupStore17019785197192635
numNetworkReqs79282124188170
🧭 User Journey Benchmarks
BenchmarkMetricMean (ms)Std Dev (ms)P75 (ms)P95 (ms)
Onboarding Import WalletimportWalletToSocialScreen2182219220
srpButtonToSrpForm8808889
confirmSrpToPwForm2102121
pwFormToMetricsScreen1401414
metricsToWalletReadyScreen1501515
doneButtonToHomeScreen8022519711204
openAccountMenuToAccountListLoaded722767580398050
total838547689488970
Onboarding New WalletcreateWalletToSocialScreen2193221223
srpButtonToPwForm1050106106
createPwToRecoveryScreen8088
skipBackupToMetricsScreen3513535
agreeButtonToOnboardingSuccess1601617
doneButtonToAssetList51649492601
total90149879986
Asset DetailsassetClickToPriceChart60218190
total60218190
Solana Asset DetailsassetClickToPriceChart4604647
total4604647
Import Srp HomeloginToHomeScreen19657420202070
openAccountMenuAfterLogin3854045
homeAfterImportWithNewWallet25801925902604
total45252145564556
Send TransactionsopenSendPageFromHome1811819
selectTokenToSendFormLoaded1911920
reviewTransactionToConfirmationPage8539852868
total89915898923
SwapopenSwapPageFromHome1033105106
fetchAndDisplaySwapQuotes45811245854602
total4680546824688
🌐 Dapp Page Load Benchmarks

Current Commit: 5097afc | Date: 2/23/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.04s (±52ms) 🟡 | historical mean value: 1.06s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 727ms (±50ms) 🟢 | historical mean value: 736ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 77ms (±10ms) 🟢 | historical mean value: 80ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.04s 52ms 1.01s 1.32s 1.11s 1.32s
domContentLoaded 727ms 50ms 701ms 1.00s 790ms 1.00s
firstPaint 77ms 10ms 60ms 152ms 88ms 152ms
firstContentfulPaint 77ms 10ms 60ms 152ms 88ms 152ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 58 Bytes (0%)
  • ui: 15.54 KiB (0.19%)
  • common: 42 Bytes (0%)

- `collectWebVitals` now calls `resetWebVitalsMetrics()` after reading,
  preventing stale metrics from carrying over when an observer doesn't
  fire in a later iteration (e.g. LCP only on initial load)
- Replace `Array<T>` with `T[]` in `buildWebVitalsSection` (eslint)
- Fix prettier formatting in `buildUiStartupSection`
- Remove trailing newline in `web-vitals-collector.ts`
@metamaskbotv2
Copy link
Contributor

metamaskbotv2 bot commented Feb 23, 2026

Builds ready [55ba7e2]
⚡ Performance Benchmarks (1382 ± 107 ms)
👆 Interaction Benchmarks
ActionMetricMean (ms)Std Dev (ms)P75 (ms)P95 (ms)
Load New Accountload_new_account27318269304
total27318269304
Confirm Txconfirm_tx6006360086010
total6006360086010
Bridge User Actionsbridge_load_page23237263281
bridge_load_asset_picker19461219297
bridge_search_token70913724726
total11355111821211
🔌 Startup Benchmarks
BuildMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
Chrome Browserify Startup Standard HomeuiStartup13821177171110714271612
load117098014389612031352
domContentLoaded116295813879511981342
domInteractive2717107212284
firstPaint160681404153214352
backgroundConnect20218424712203227
firstReactRender19123942025
initialActions105112
loadScripts97677012099610221153
setupStore1373451522
numNetworkReqs312290202286
Chrome Browserify Startup Power User HomeuiStartup1947134210526121318392349
load11581016188416911511569
domContentLoaded1143981178016311391560
domInteractive35191662534108
firstPaint212681801180252305
backgroundConnect4242604902643326645
firstReactRender25165562836
initialActions105112
loadScripts91878215371549111320
setupStore1885991939
numNetworkReqs62351682868134
Chrome Webpack Startup Standard HomeuiStartup87371311781089241122
load731620109697773896
domContentLoaded725615108996769892
domInteractive2816142212380
firstPaint1106534854125206
backgroundConnect291993113147
firstReactRender20124782336
initialActions106112
loadScripts723613108795767885
setupStore1253851422
numNetworkReqs3122104212589
Chrome Webpack Startup Power User HomeuiStartup1198901171815712801497
load7026281042966911012
domContentLoaded6936231035966801004
domInteractive34191382632114
firstPaint1316746274161259
backgroundConnect16512936638160243
firstReactRender22173132427
initialActions101011
loadScripts691621102494678995
setupStore1354671422
numNetworkReqs1063326653136249
Firefox Browserify Startup Standard HomeuiStartup16281386291621316332004
load13691187267018113981653
domContentLoaded13681187267018113981652
domInteractive8833128713398186
firstPaint------
backgroundConnect5827171236393
firstReactRender13111911415
initialActions102012
loadScripts13441168264717913681628
setupStore187190251537
numNetworkReqs3119101202591
Firefox Browserify Startup Power User HomeuiStartup27532002617149728173539
load15991219489641416082138
domContentLoaded15991219489641416082137
domInteractive13535667128120421
firstPaint------
backgroundConnect282116933220274874
firstReactRender201469111960
initialActions204123
loadScripts15631194486740915742048
setupStore1507770202138642
numNetworkReqs70291853886144
Firefox Webpack Startup Standard HomeuiStartup17291437343534217122071
load14681162316732114731677
domContentLoaded14681161316732214721676
domInteractive108281588157131181
firstPaint------
backgroundConnect63262384168152
firstReactRender15122631523
initialActions102012
loadScripts14431146307331614441625
setupStore197152221560
numNetworkReqs311990182784
Firefox Webpack Startup Power User HomeuiStartup26922004438639528533490
load15861325273629117442209
domContentLoaded15851323273629217442209
domInteractive165301385211122621
firstPaint------
backgroundConnect2981091219235338882
firstReactRender21156782330
initialActions213123
loadScripts15521275271828916902157
setupStore14781199214166614
numNetworkReqs68212314080129
🧭 User Journey Benchmarks
BenchmarkMetricMean (ms)Std Dev (ms)P75 (ms)P95 (ms)
Onboarding Import WalletimportWalletToSocialScreen2180218219
srpButtonToSrpForm8919091
confirmSrpToPwForm2102121
pwFormToMetricsScreen1501515
metricsToWalletReadyScreen1511616
doneButtonToHomeScreen8742159771190
openAccountMenuToAccountListLoaded666516768116831
total79729679738125
Onboarding New WalletcreateWalletToSocialScreen2171217218
srpButtonToPwForm1031103104
createPwToRecoveryScreen8088
skipBackupToMetricsScreen3413435
agreeButtonToOnboardingSuccess1501515
doneButtonToAssetList687134800864
total106613511761244
Asset DetailsassetClickToPriceChart51135774
total51135774
Solana Asset DetailsassetClickToPriceChart4514546
total4514546
Import Srp HomeloginToHomeScreen21157221972207
openAccountMenuAfterLogin4324247
homeAfterImportWithNewWallet277235631863192
total493442554405440
Send TransactionsopenSendPageFromHome3833943
selectTokenToSendFormLoaded1911920
reviewTransactionToConfirmationPage8483848853
total9033907907
SwapopenSwapPageFromHome12826149154
fetchAndDisplaySwapQuotes562586263596386
total575284664846508
🌐 Dapp Page Load Benchmarks

Current Commit: 55ba7e2 | Date: 2/23/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.06s (±74ms) 🟡 | historical mean value: 1.06s ⬆️ (historical data)
  • domContentLoaded-> current mean value: 744ms (±70ms) 🟢 | historical mean value: 736ms ⬆️ (historical data)
  • firstContentfulPaint-> current mean value: 78ms (±13ms) 🟢 | historical mean value: 80ms ⬇️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.06s 74ms 1.01s 1.38s 1.31s 1.38s
domContentLoaded 744ms 70ms 699ms 1.03s 969ms 1.03s
firstPaint 78ms 13ms 64ms 196ms 92ms 196ms
firstContentfulPaint 78ms 13ms 64ms 196ms 92ms 196ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 58 Bytes (0%)
  • ui: 18.34 KiB (0.22%)
  • common: 42 Bytes (0%)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size-XL team-extension-platform Extension Platform team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants