> For an index of all Botscent documentation, see https://botscent.nibnalin.me/llms.txt.

# Performance

What the page half costs a page, measured in a real browser against the same page without it.

This page gives what the page half costs a page. The numbers come from a real browser, against the same page without the library. `npm run perf` in the repository reproduces every number. The CI `perf` job fails when a number leaves its budget.

## Summary

The reference device is defined in [Method](#method). Its CPU is about the CPU of a budget phone. Each row compares the same page with and without the library.

| What                             | Measured                                                                                                     | Target                                       |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------ | -------------------------------------------- |
| Start-up: the script and `start` | **4.6–5.5 ms**                                                                                               | under 2 ms: **not met**                      |
| Long tasks                       | none                                                                                                         | none: met                                    |
| First paint and LCP, light page  | **+4 ms** (the paint waits for start-up)                                                                     | measured, no number                          |
| First paint and LCP, heavy page  | no change                                                                                                    | measured, no number                          |
| CLS                              | no change (0)                                                                                                | no DOM writes: met                           |
| Work per trusted input           | 0.010–0.018 ms median; 0.24–0.58 ms at the 95th percentile, when it reads the markers (at most every 300 ms) | microseconds: met except the marker read     |
| INP, scripted visit              | no change                                                                                                    | measured, no number                          |
| Idle, visible                    | 8 callbacks in 30 s, 1.1–2.4 ms in all                                                                       | nothing beyond the triggers: met             |
| Idle, hidden                     | 6 callbacks in 30 s, no DOM reads                                                                            | the slow sample runs only while visible: met |
| DOM churn, every frame           | 0.74–0.89 ms per second. The 250 ms debounce holds at 4 reads a second                                       | met                                          |
| A day left open                  | 17,280 callbacks. Heap 103 KB at rest, no growth after the JIT settles                                       | met                                          |
| Requests, storage, DOM writes    | none                                                                                                         | none: met                                    |

## Start-up

Start-up is 4.6–5.5 ms on the reference device, and 1.3 ms unthrottled. It is spread across module evaluation, compilation, `start` and the descriptor and source reads. No single part dominates. The 2 ms target stays unmet on the reference device, which is slower than a mid-range phone.

Start-up does no work for the two cloud-browser rules. Muse's renderer read happens only after its other clauses match, in a later task. Grok Bot's computer check runs once, at the first idle moment within 300 ms. On a machine that is not Linux x86_64 with Chrome 139 or later, it returns after it reads the platform and the user agent.

The first measurements, on 2–3 October, found start-up at 27.5–31.4 ms, with a long task on most light-page loads. Nearly all of it was the first `Intl.DateTimeFormat` in a document, which loads the time-zone data. An earlier Grok Bot rule read the time zone at start on every page. The current check reads it only on the cloud host, after three other signs match.

## Where the remaining costs are

* **The marker read.** The `markers` probe looks for the ChatGPT badge only in `<head>`, and for six Claude for Chrome ids by id. Its cost does not grow with the size of the page. It is the 95th percentile of the time per input.
* **The hidden interval.** While the document is hidden, the slow sample still wakes every 5 s. Each callback checks `visibilityState` and returns without a DOM read.

## Method

### The reference device

Every Chromium measurement runs with the CPU throttled through the DevTools protocol method `Emulation.setCPUThrottlingRate`. The reference device is an Apple M3 Max throttled 4x, the slowdown that Lighthouse applies for its mobile profile. In single-thread speed, that is about a budget Android phone.

Other machines emulate the same device. Before it measures, the harness times a fixed workload, then sets the throttle to `4 × 7.0 ms / its own median`. Calibration evens out CPU speed. It does not make a laptop into a phone, so the budgets keep headroom.

### Attribution

The harness traces each load and reads the library's time from the trace by script URL. That time is the library's `EvaluateScript` and every `FunctionCall` into its code. `EvaluateScript` covers compilation, module evaluation and the synchronous `start`.

### The pages

* **article**: a small static page with 30 paragraphs and a hero image as the LCP element. It has a search box that filters a 200-item list, and a button that adds a row.
* **feed**: a heavier page of about 10,000 nodes. On every animation frame, it adds a toast under `<body>` and removes the last one. Every 100 ms, it updates text deep in the tree.

The library loads as `<script defer src="/botscent.js">` from the built `dist/botscent.js`. The variant without the library is the same page without that tag. Chromium runs with `navigator.webdriver` false, so the library takes the path that it takes for a person.

### The measurements

* **Page loads:** 11 per page and variant, interleaved, each in a fresh browser context. The harness records start-up, long tasks, FCP, LCP, CLS, every request and every DOM change.
* **Cold start:** 5 browser launches, with the same start-up attribution in the first document.
* **Input:** 5 scripted visits per page and variant, with trusted clicks, typing and wheel scrolls through CDP.
* **DOM churn:** 2 windows of 10 s per variant on the feed page.
* **Idle:** the article page for 30 s visible, then 30 s hidden. The harness also checks requests, storage and cookies at the end.
* **A day left open:** each interval callback runs as often as it would in 24 hours, four times over. The harness reads the heap and the listeners after each simulated day.
* **Other engines:** start-up in Firefox and WebKit, unthrottled, as a record only.

Every figure is a median. The reference runs used macOS 26.1, Node 24.13 and Playwright 1.63 (Chromium 153.0.8010.12), two runs on 4 October 2026.

## Limits

* **Engines.** Only Chromium is gated, because only Chromium can be throttled and traced. Firefox and WebKit are recorded for start-up only.
* **Devices.** Throttling emulates a slower CPU, not a phone.
* **Pages and visits.** The pages are synthetic, and every load is a first visit with no code cache.
* **Entry points.** The measurements use the script build. The module entries `botscent` and `botscent/auto` run the same code through the application's bundler. They are not measured separately.
* **The simulated day.** The harness calls the real callbacks rather than waiting. It does not show effects that only real hours would show.

## Run it

Build the library, then run the harness:

```sh title="Terminal"
npm run build
npm run perf
```

One run takes about 5 minutes. It prints each number beside its budget. `npm run perf -- --json results.json` also writes every sample.

## Related

* [How it works](/docs/how-it-works): what each probe looks for, and when.
* [Privacy](/docs/privacy): every probe and every header that Botscent reads.
