> Meticulous records real user sessions from a web app and replays them against each commit to catch visual regressions.
>
> Full AI-readable docs index (complete project setup journey + every page as markdown): https://app.meticulous.ai/llms.txt

# Performance API Reference

> **info: Actively under development**
> The Performance API is under active development and its surface may evolve. If you're interested in using it or have feedback, we'd love to hear from you — reach out at [support@meticulous.ai](mailto:support@meticulous.ai).

Meticulous can feed frontend performance data into your existing monitoring systems — such as Datadog, Grafana, New Relic, or any custom analytics pipeline — so you can track how rendering speed, JavaScript execution time, and memory usage change across commits.

When Meticulous replays a recorded session, it stubs browser time and performance APIs to ensure deterministic behavior. The Performance API provides access to **real, non-stubbed** browser performance primitives that you can report directly to your monitoring backend.

---

## Overview

During a Meticulous replay:

- `performance.now()`, `Date.now()`, and other timing APIs return **virtual** (deterministic) values.
- `performance.memory` returns **fixed** values.
- `performance.measureUserAgentSpecificMemory()` returns **fixed** values.
- `PerformanceObserver` is stubbed.
- `PressureObserver` is stubbed.
- `setTimeout`, `setInterval`, `clearTimeout`, and `clearInterval` schedule callbacks on **virtual** (deterministic) time.

The Performance API, available on `window.Meticulous.replay.native`, bypasses this stubbing and returns **real** values. This lets you measure actual frontend performance and feed it into dashboards like Datadog, Grafana, or your own monitoring system.

> **warning: Frontend performance only**
> This API measures **frontend performance only**: rendering time, JavaScript execution, and memory usage.
>
> During replays all network traffic is mocked using previously recorded responses — network requests return instantly with cached data. **Do not use these APIs to measure network latency, API response times, or backend performance.** Those measurements are not meaningful during a replay.

---

## When to Use

Only collect and report metrics when `isBenchmarkableReplay` is `true`. This flag indicates the replay was executed under conditions where performance data is meaningful.

```typescript
if (window.Meticulous?.replay?.isBenchmarkableReplay) {
  // Safe to collect and report performance metrics
}
```

---

## Available APIs

All APIs live on `window.Meticulous.replay.native`.

### `native.performance.now()`

Returns the real elapsed time in milliseconds, bypassing Meticulous's virtual time. Wraps the native [`Performance.now()`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now).

**Returns**: `number`

```typescript
const realElapsed = window.Meticulous.replay.native.performance.now();
```

Use this wherever you would normally use `performance.now()` to measure durations:

```typescript
const perf = window.Meticulous?.replay?.isBenchmarkableReplay
  ? window.Meticulous.replay.native.performance
  : window.performance;

const start = perf.now();
doExpensiveWork();
const duration = perf.now() - start;
```

---

### `native.performance.memory`

**Type**: `{ jsHeapSizeLimit: number; totalJSHeapSize: number; usedJSHeapSize: number } | undefined`

Returns actual browser memory usage, bypassing the fixed values Meticulous stubs in. Wraps the native [`Performance.memory`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/memory).

| Property | Type | Description |
|----------|------|-------------|
| `jsHeapSizeLimit` | `number` | Maximum heap size in bytes |
| `totalJSHeapSize` | `number` | Total allocated heap in bytes |
| `usedJSHeapSize` | `number` | Currently used heap in bytes |

```typescript
const mem = window.Meticulous.replay.native.performance.memory;
if (mem) {
  console.log("Heap used:", mem.usedJSHeapSize);
}
```

---

### `native.performance.measureUserAgentSpecificMemory()`

**Type**: `(() => Promise<MemoryMeasurement>) | undefined`

Returns a promise that resolves to a detailed, attributed breakdown of the memory used by all JavaScript realms (the page, its iframes, and its workers), bypassing Meticulous's deterministic stub. Wraps the native [`Performance.measureUserAgentSpecificMemory()`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/measureUserAgentSpecificMemory).

The resolved value has the following shape:

| Property | Type | Description |
|----------|------|-------------|
| `bytes` | `number` | Total memory used, in bytes |
| `breakdown` | `Array<{ bytes: number; attribution: unknown[]; types: string[] }>` | Per-realm/per-type breakdown of `bytes` |

`breakdown[].types` is an open-ended set that varies by browser, build, and the page itself (e.g. `"JavaScript"`, `"DOM"`, `"Shared"`, `"WebAssembly"`, `"Detached"`, ...). A single breakdown entry can carry multiple types, so per-type buckets generally do not sum to `bytes` — use `bytes` for the canonical total.

```typescript
const measure = window.Meticulous.replay.native.performance
  .measureUserAgentSpecificMemory;

if (measure) {
  const measurement = await measure();
  console.log("Total bytes:", measurement.bytes);
  for (const entry of measurement.breakdown) {
    console.log(entry.types, entry.bytes);
  }
}
```

> **warning: Cross-origin isolation and availability**
> The native `measureUserAgentSpecificMemory()` is normally only callable from a [cross-origin isolated](https://developer.mozilla.org/en-US/docs/Web/API/Window/crossOriginIsolated) context. **Meticulous replays do not run in a cross-origin isolated context**, so this API may be `undefined` (or throw a `SecurityError` when called) and you should always check for its presence and `try/catch` around the call.
>
> However, depending on how Meticulous proxies and serves your application during replay, the underlying browser context may end up satisfying the security checks even when `window.crossOriginIsolated` is `false`. As a result, on some projects this function is callable and returns real measurements. Contact the Meticulous engineering team for more information about whether this API is available for your project.

---

### `native.PerformanceObserver`

**Type**: `typeof PerformanceObserver`

The native [`PerformanceObserver`](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver) constructor for observing real performance entries (e.g., `navigation`, `resource`, `measure`), unaffected by virtual time.

```typescript
const observer = new window.Meticulous.replay.native.PerformanceObserver(
  (list) => {
    for (const entry of list.getEntries()) {
      reportMetric(entry.name, entry.duration);
    }
  }
);
observer.observe({ entryTypes: ["measure", "navigation"] });
```

---

### `native.PressureObserver`

**Type**: `typeof PressureObserver | undefined`

The native [`PressureObserver`](https://developer.mozilla.org/en-US/docs/Web/API/PressureObserver) constructor from the [Compute Pressure API](https://developer.mozilla.org/en-US/docs/Web/API/Compute_Pressure_API), exposed under `native` for symmetry with the other performance primitives.

Use it to observe the pressure state of system resources such as the CPU (`"nominal"`, `"fair"`, `"serious"`, `"critical"`) and correlate it with your frontend performance metrics.

```typescript
const replay = window.Meticulous?.replay;
const PressureObserver = replay?.native.PressureObserver;

if (replay?.isBenchmarkableReplay && PressureObserver) {
  const observer = new PressureObserver((records) => {
    for (const record of records) {
      reportPressure({
        source: record.source,
        state: record.state,
        time: record.time,
      });
    }
  });

  observer.observe("cpu", { sampleInterval: 1000 }).catch((err) => {
    console.warn("Failed to observe CPU pressure", err);
  });
}
```

The callback receives `MeticulousPressureRecord[]` entries, each with a `source` (`MeticulousPressureSource`, currently `"cpu"`), a `state` (`MeticulousPressureState`), and a `time` (`DOMHighResTimeStamp`). Types are exported from [`@alwaysmeticulous/replay-browser-scripts-api`](/docs/how-to/typescript-types) as `MeticulousPressureObserver`, `MeticulousPressureObserverConstructor`, `MeticulousPressureRecord`, `MeticulousPressureSource`, and `MeticulousPressureState`.

---

### `native.setTimeout()`, `native.setInterval()`, `native.clearTimeout()`, and `native.clearInterval()`

**Types**: Same signatures as the native [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout), [`setInterval`](https://developer.mozilla.org/en-US/docs/Web/API/setInterval), [`clearTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/clearTimeout), and [`clearInterval`](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval) functions.

During a replay, the global `setTimeout` and `setInterval` are intercepted and schedule callbacks against Meticulous's virtual time. The versions on `window.Meticulous.replay.native` use the **real** browser timer functions instead, so callbacks fire after actual wall-clock delays.

Use these when you need real-time scheduling — for example, to defer performance metric collection until after the main thread has settled:

```typescript
const replay = window.Meticulous?.replay;
if (replay?.isBenchmarkableReplay) {
  replay.native.setTimeout(() => {
    reportPerformanceMetrics();
  }, 0);
}
```

> **warning: Use with extreme care**
> These functions run against **real wall-clock time** and their callbacks are **not** synchronized with Meticulous's virtual event loop or screenshot timing.
>
> **Avoid any callback behaviour that has a visual impact** — including DOM mutations, CSS changes, animations, scrolls, focus changes, toasts, modals, or any other UI updates. Such side effects can desynchronize the replay from its recorded state and cause visual-test failures or flaky results.
>
> Restrict callbacks to **non-visual** work such as collecting metrics, sending analytics payloads, or other read-only instrumentation. Never use these timers to drive UI updates, lazy-load visible content, or trigger layout during a replay.

---

## Metadata

When reporting metrics you'll typically want to attach context about what is being tested. Two properties on `window.Meticulous.replay` provide this:

### `commitUnderTest`

**Type**: `{ sha: string; baseCommitSha: string | null; branchName: string | null; date: string | null } | undefined`

| Property | Type | Description |
|----------|------|-------------|
| `sha` | `string` | Full commit SHA being tested |
| `baseCommitSha` | `string \| null` | Base commit SHA this test run is compared against (typically the commit on the main branch that the PR branch forked from). `null` when the test run is not associated with a PR, or when no base commit was specified or could be determined |
| `branchName` | `string \| null` | Git branch name |
| `date` | `string \| null` | Commit date in ISO 8601 format |

### `sessionBeingReplayed`

**Type**: `{ id: string }`

The ID of the recorded session being replayed.

### `browser`

**Type**: `{ version: string }`

Information about the Chrome/Chromium build that is driving the replay.
Useful for tagging reported metrics so that performance dashboards can be
sliced by browser version — rendering speed, JavaScript execution time, and
memory usage can shift meaningfully between Chrome releases, and aggregating
across versions can mask regressions.

| Property | Type | Description |
|----------|------|-------------|
| `version` | `string` | The Chrome/Chromium version, e.g. `"139.0.7258.5"`. The leading product label (`"Chrome/"`, `"HeadlessChrome/"`, ...) is stripped, so the value can be parsed directly as a dotted version number. Falls back to `"unknown"` only in the very rare case Meticulous could not read the version from the underlying browser. |

```typescript
const replay = window.Meticulous?.replay;
if (replay) {
  console.log("Replay running on Chrome", replay.browser.version);
}
```

---

## Sending Data to Analytics

During replays, Meticulous intercepts and mocks network requests. To let your analytics requests pass through, add the `meticulous-passthrough` header set to `"true"`:

```typescript
fetch("https://analytics.example.com/metrics", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "meticulous-passthrough": "true",
  },
  body: JSON.stringify(payload),
});
```

Without this header, the request will be intercepted by the network stubbing layer and will not reach your analytics endpoint.

---

## Full Example

```typescript
const reportPerformanceMetrics = () => {
  const replay = window.Meticulous?.replay;
  if (!replay?.isBenchmarkableReplay) {
    return;
  }

  const elapsed = replay.native.performance.now();
  const memory = replay.native.performance.memory;
  const commit = replay.commitUnderTest;

  fetch("https://analytics.example.com/metrics", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "meticulous-passthrough": "true",
    },
    body: JSON.stringify({
      elapsedMs: elapsed,
      heapUsedBytes: memory?.usedJSHeapSize,
      heapTotalBytes: memory?.totalJSHeapSize,
      commitSha: commit?.sha,
      baseCommitSha: commit?.baseCommitSha,
      branch: commit?.branchName,
      sessionId: replay.sessionBeingReplayed.id,
      chromeVersion: replay.browser.version,
    }),
  });
};
```

---

## Data Noise

Treat performance data collected from Meticulous replays as **noisy**. Two factors introduce variance:

- **Hardware differences**: Replays may execute on different machines with different CPU, memory, and disk characteristics. Absolute numbers will vary across runs.
- **Network mocking overhead**: Meticulous intercepts and mocks all network requests during replay. The mocking mechanism itself introduces some overhead that does not exist in production.

Because of this, focus on **trends over time** rather than individual data points. Aggregate across multiple replays and commits to identify meaningful regressions or improvements.

---

## Requirements

- **Gate on `isBenchmarkableReplay`**: Always check this before collecting metrics. When `false`, the replay conditions do not guarantee meaningful numbers and results should be discarded.
- **Do not render metrics in the UI**: Displaying real performance values in the DOM will cause visual differences between runs. Report them to external systems only.
- **Native timers are for non-visual work only**: `native.setTimeout` and `native.setInterval` bypass virtual time. Avoid any callback behaviour with a visual impact — DOM updates, animations, scrolls, focus changes, and similar side effects can break visual tests.
- **Use the passthrough header**: Without `meticulous-passthrough: "true"`, analytics requests will be intercepted and mocked, and your data will never reach your analytics endpoint.
- **Frontend metrics only**: Rendering, JavaScript execution, and memory are meaningful. Network and backend latency are not, because all requests return mocked responses.

---

## See Also

- [window.Meticulous API](/docs/how-to/window-meticulous-object) — detecting test mode, pause/resume, custom data
- [TypeScript Types](/docs/how-to/typescript-types) — importable type definitions for `window.Meticulous`
