pagepulse
v0.1.1
Published
Browser performance metrics tracker with Lit web components for live graphs.
Maintainers
Readme
pagepulse
pagepulse is an ESM TypeScript library for capturing browser performance metrics and rendering live Lit dashboard charts.
Try the Demo
Live demo: https://davidhanson90.github.io/pagepulse/
Or run the harness locally:
npm install
npm startThen open the local URL shown in your terminal (Vite default is usually http://localhost:5173).
Features
- ESM package output with TypeScript declarations
- Tracker API for HTTP, long tasks, DOM, web vitals, memory, FPS, resources, LoAF, connection, navigation, and errors
- Reusable
<pagepulse-dashboard>,<pagepulse-chart>,<pagepulse-waterfall>, and<pagepulse-data-table>web components - Export latest gauges / time-series / resource rows as JSON or CSV (
downloadData,snapshotToTable) - Dark / light / auto theming via a
themeattribute and--pagepulse-*CSS variables - Soft-failing collectors when browser APIs are unavailable
Angular / Zone.js
When Zone.js is present (typical Angular apps), pagepulse schedules its sample loop, timers, requestAnimationFrame, PerformanceObserver callbacks, and related event handlers outside Angular’s zone (Zone.root / unpatched timer symbols). That avoids thrashing change detection on every metric tick.
- No
zone.jsor@angular/coredependency — detection is optional viaglobalThis.Zoneonly. - App code that
subscribe()s from inside an Angular component may still wantNgZone.runOutsideAngular(...)around its own handlers if those handlers update Angular state frequently.
Installation
npm install pagepulseUsage
1. Import the library
import { createTracker } from "pagepulse";Importing pagepulse registers the Lit custom elements.
2. Create and start a tracker
const tracker = createTracker({
sampleIntervalMs: 1000,
retentionMs: 5 * 60 * 1000,
collectors: {
http: true,
longTasks: true,
dom: true,
webVitals: true,
memory: true,
fps: true,
loaf: true,
connection: true,
navigation: true,
errors: true
}
});
tracker.start();
tracker.subscribe((snapshot) => {
console.log(snapshot.latest);
});3. Render the dashboard
<pagepulse-dashboard id="dash" theme="dark"></pagepulse-dashboard>theme accepts dark (default), light, or auto (follow prefers-color-scheme).
const dash = document.getElementById("dash");
if (dash) {
dash.tracker = tracker;
}If you do not set .tracker, the dashboard uses the default tracker from the latest createTracker() call.
4. Stop tracking
tracker.stop();5. Resource waterfall
<pagepulse-waterfall> renders a horizontal Resource Timing timeline (one row per resource). It is included under the metric grid on <pagepulse-dashboard>, and can also be used standalone:
<pagepulse-waterfall id="wf" theme="dark"></pagepulse-waterfall>const tracker = createTracker({
firstPartyDomains: ["static.example.com"], // optional allow-list (page host is always 1st-party)
maxResourceEntries: 150
});
tracker.start();
const wf = document.getElementById("wf");
if (wf) {
wf.tracker = tracker;
wf.theme = "dark";
// optional: wf.firstPartyDomains = ["static.example.com"];
}Legend
| Visual | Meaning |
| --- | --- |
| Blue / purple / green / coral / gold / gray bars | Initiator: script, css/link, img, fetch/xhr, font, other |
| Striped bar | Third-party host |
| Light outline on bar | Cached heuristic (transferSize === 0 with encoded/decoded body size) |
Hover a row (native title tooltip) for URL, type, duration, size, 1p/3p, and cached.
tracker.getResources(); // ResourceTimingRow[]
tracker.subscribeResources(cb); // unsubscribe function
tracker.clearResources(); // clear rolling bufferCollector flag: collectors.resources (default true). Soft-fails when PerformanceObserver / Resource Timing is unavailable. Buffer clears best-effort on soft navigations (soft-navigations observer + popstate).
6. Raw data table & download
Build tabular payloads from a snapshot (or directly from the tracker) and trigger a browser download:
import {
createTracker,
snapshotToTable,
seriesToTable,
resourcesToTable,
downloadData,
tableToCsv
} from "pagepulse";
const tracker = createTracker();
tracker.start();
const gauges = tracker.getDataTable();
// { columns: ["metric","latest","description","pointCount","series"], rows: [...] }
const series = tracker.getSeriesTable(); // long-form: metric, t, v
const resources = tracker.getResourcesTable(); // Resource Timing rows
downloadData(gauges, { format: "json", filename: "pagepulse-metrics.json" });
downloadData(gauges, { format: "csv", filename: "pagepulse-metrics.csv" });
// Or build from a Snapshot you already have:
const snap = tracker.getSnapshot();
snapshotToTable(snap);
seriesToTable(snap);
resourcesToTable(tracker.getResources());
tableToCsv(gauges); // stringdownloadData uses Blob + an object URL and soft-fails (returns false) when DOM download APIs are unavailable (e.g. Node). CSV requires a DataTable ({ columns, rows }); JSON accepts any serializable value.
<pagepulse-data-table>
<pagepulse-data-table id="raw" theme="dark"></pagepulse-data-table>const raw = document.getElementById("raw");
if (raw) {
raw.tracker = tracker;
raw.theme = "dark"; // or "light" | "auto"
}The component shows a scrollable table of current metrics (name, latest value, description, point count, series JSON), with Download JSON / Download CSV buttons and a Metrics / Resources tab toggle. In the harness, enable Show raw data table in the toolbar.
Metric title help
Metric title help
Hover a dashboard panel title to see a short description of what that metric means. Descriptions also appear as the native browser tooltip via the title attribute.
Theming
pagepulse ships two built-in visual themes — dark and light — plus auto to follow the OS.
Theme previews
| Dark (theme="dark") | Light (theme="light") |
| --- | --- |
|
|
|
Theme options
| Value | Result |
| --- | --- |
| unset / dark | Dark palette (default, original 0.1.0 look) |
| light | Full light palette |
| auto | Uses light when the OS prefers light (prefers-color-scheme: light), otherwise dark |
<pagepulse-dashboard>, <pagepulse-chart>, <pagepulse-waterfall>, and <pagepulse-data-table> accept a reflected theme attribute/property. The dashboard forwards theme to nested charts and the waterfall so colors stay in sync.
How to enable a theme
Option A — HTML attribute (simplest)
<!-- Dark (default if omitted) -->
<pagepulse-dashboard theme="dark"></pagepulse-dashboard>
<!-- Light -->
<pagepulse-dashboard theme="light"></pagepulse-dashboard>
<!-- Follow the user's OS preference -->
<pagepulse-dashboard theme="auto"></pagepulse-dashboard>Option B — JavaScript property
import { createTracker } from "pagepulse";
const tracker = createTracker();
tracker.start();
const dash = document.querySelector("pagepulse-dashboard");
if (dash) {
dash.tracker = tracker;
dash.theme = "light"; // or "dark" | "auto"
}Option C — Try it in the harness
npm install
npm startOpen the local URL, then use the Theme dropdown (Dark / Light / Auto) in the toolbar. That sets theme on the dashboard live.
Customizing colors
Tokens live on :host and can be overridden per instance (works with any theme):
<pagepulse-dashboard
theme="light"
style="--pagepulse-accent: #c026d3; --pagepulse-bg: #faf6ff;">
</pagepulse-dashboard>| Variable | Role | Dark default | Light default |
| --- | --- | --- | --- |
| --pagepulse-bg | Host background | #0b1220 | #f3f5f8 |
| --pagepulse-text | Primary text | #e8eef7 | #1a2433 |
| --pagepulse-muted | Secondary text | #8b9bb0 | #5c6d82 |
| --pagepulse-title | Panel titles | #9fb3c8 | #3d5270 |
| --pagepulse-panel-bg | Panel surface | #121a2b | #ffffff |
| --pagepulse-panel-border | Panel border | #243149 | #d3dce8 |
| --pagepulse-chart-bg | Chart / canvas background | #0d1524 | #e8eef6 |
| --pagepulse-grid | Chart grid lines | #1c2940 | #c9d4e4 |
| --pagepulse-status | Header status text | #9fb3c8 | #5c6d82 |
| --pagepulse-empty | Empty-chart label | #4a5a70 | #7a8b9e |
| --pagepulse-accent | Default series color | #5b9cff | #2563eb |
Canvas drawing reads these via getComputedStyle (background, grid, empty-state text, and the default series color when color is unset).
Metrics
httpInFlight— fetch / XHR wrappersrequestSize— Resource Timing transfer/encoded sizelongTaskDuration— PerformanceObserver longtaskdomNodeCount/domMaxDepth— periodic DOM walklcp/cls/inp/ttfb— web vitalsjsHeapUsed/jsHeapLimit—performance.memorywhen presentfps— requestAnimationFrameresourceScript/resourceCss/resourceImg/resourceFetch— resource countsloafDuration/loafScriptDuration/loafStyleDuration— Long Animation Frames (PerformanceObservertypelong-animation-frame; Chromium)connectionRtt/connectionDownlink/connectionEffectiveType— Network Information API (navigator.connection; effectiveType ordinal 0–4)navDomContentLoaded/navLoad— Navigation Timing (hard navigation)softNavCount/softNavDuration— soft navigations viasoft-navigationsobserver andhistory/popstatehookserrorCount/rejectionCount— cumulativewindowerrorandunhandledrejectioncounts
Scripts
npm run build- compile library todist/npm run test- run Vitest testsnpm run test:coverage- run tests with coverage reports incoverage/npm run build:verify- run coverage, lint, harness build, and typechecknpm run pack:check- show package contents usingnpm pack --dry-run
Development
npm install
npm startLicense
MIT
