Performance API Reference
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.
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.memoryreturns fixed values.performance.measureUserAgentSpecificMemory()returns fixed values.PerformanceObserveris stubbed.PressureObserveris stubbed.setTimeout,setInterval,clearTimeout, andclearIntervalschedule 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.
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.
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().
Returns: number
const realElapsed = window.Meticulous.replay.native.performance.now();
Use this wherever you would normally use performance.now() to measure durations:
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.
| Property | Type | Description |
|---|---|---|
jsHeapSizeLimit | number | Maximum heap size in bytes |
totalJSHeapSize | number | Total allocated heap in bytes |
usedJSHeapSize | number | Currently used heap in bytes |
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().
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.
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);
}
}
Cross-origin isolation and availability
The native measureUserAgentSpecificMemory() is normally only callable from a cross-origin isolated 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 constructor for observing real performance entries (e.g., navigation, resource, measure), unaffected by virtual time.
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 constructor from the 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.
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 as MeticulousPressureObserver, MeticulousPressureObserverConstructor, MeticulousPressureRecord, MeticulousPressureSource, and MeticulousPressureState.
native.setTimeout(), native.setInterval(), native.clearTimeout(), and native.clearInterval()
Types: Same signatures as the native setTimeout, setInterval, clearTimeout, and 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:
const replay = window.Meticulous?.replay;
if (replay?.isBenchmarkableReplay) {
replay.native.setTimeout(() => {
reportPerformanceMetrics();
}, 0);
}
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. |
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":
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
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. Whenfalse, 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.setTimeoutandnative.setIntervalbypass 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 — detecting test mode, pause/resume, custom data
- TypeScript Types — importable type definitions for
window.Meticulous