npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

gpu-perf-agent

v0.1.0

Published

Agent-ready WebGPU/WebGL2 performance profiler: Node-callable benchmark, trace, and memory reports with verdicts for automated optimization loops. Ships an agent skill.

Readme

gpu-perf-agent

An agent-ready WebGPU/WebGL2 performance profiler. It ships as an npm CLI plus a drop-in agent skill, so AI coding assistants (Claude, Gemini, Codex, Copilot, Cursor, ...) can profile a page, read a one-word verdict, apply an optimization, and prove the win with a regression-checked compare — autonomously.

Humans and CI get the same thing: repeatable performance/memory artifacts for tuning browser GPU workloads.

The default runner is a fast direct Chrome DevTools Protocol harness. It launches Chrome/Chromium itself and talks CDP over WebSocket, avoiding Playwright's context and driver layers. A Playwright runner remains exported as runPlaywrightReport() for compatibility.

Give the Skill to Your Agent

The skill definition lives in skill/SKILL.md and is included in the npm tarball (node_modules/gpu-perf-agent/skill/SKILL.md). Install the package, then drop the skill where your agent discovers skills:

npm install -D gpu-perf-agent

Claude Code — project-scoped (recommended, travels with the repo):

mkdir -p .claude/skills/webgpu-performance-profiling
cp node_modules/gpu-perf-agent/skill/SKILL.md .claude/skills/webgpu-performance-profiling/SKILL.md

Or user-wide with ~/.claude/skills/webgpu-performance-profiling/.

OpenAI Codex CLI:

mkdir -p ~/.codex/skills/webgpu-performance-profiling
cp node_modules/gpu-perf-agent/skill/SKILL.md ~/.codex/skills/webgpu-performance-profiling/SKILL.md

Or reference the skill from your AGENTS.md: "For GPU performance profiling, follow node_modules/gpu-perf-agent/skill/SKILL.md".

Gemini CLI / Antigravity:

mkdir -p ~/.gemini/skills/webgpu-performance-profiling
cp node_modules/gpu-perf-agent/skill/SKILL.md ~/.gemini/skills/webgpu-performance-profiling/SKILL.md

Or reference it from GEMINI.md the same way as AGENTS.md.

GitHub Copilot (VS Code) — project-scoped:

mkdir -p .github/skills/webgpu-performance-profiling
cp node_modules/gpu-perf-agent/skill/SKILL.md .github/skills/webgpu-performance-profiling/SKILL.md

Cline / Roo Code / Cursor — paste the contents of skill/SKILL.md into your rules file (.clinerules, .cursorrules, or custom instructions).

Once installed, ask your agent things like "profile http://localhost:5173 and fix the biggest GPU bottleneck" — the skill teaches it the full baseline → optimize → compare loop.

What It Captures

  • Fast direct-CDP benchmark samples through Chrome/Chromium.
  • Optional Playwright runner for compatibility.
  • Optional GPU timestamp helpers for precise WebGPU/WebGL2 command timing.
  • WebGPU/WebGL2 capability metadata.
  • JS heap and browser CDP metrics.
  • Optional Chrome trace summaries for GPU, frame, and memory-infra events.
  • Optional declared GPU allocation tracking for buffers/textures created by your app.
  • Optional macOS xctrace capture using Xcode Instruments templates such as Metal System Trace.

Important limitation: the web platform does not expose exact VRAM residency for WebGPU/WebGL2. For deep memory analysis, use all three layers together: declared allocation tracking in the app, Chrome trace memory-infra summaries, and native Instruments captures on macOS.

Install

From npm:

npm install -D gpu-perf-agent
npx gpu-perf-agent doctor

Or run one-off without installing:

npx gpu-perf-agent doctor

From a checkout of this repository:

npm install
npm run doctor

The fast runner prefers an installed Chrome/Chromium. If it cannot find one, it falls back to Playwright's bundled Chromium path when available. Playwright itself is an optional peer dependency — only needed for the compatibility runner or its bundled browser:

npm install playwright
npx playwright install chromium

CLI

Run a report against a local file:

node ./src/cli.js run --file examples/webgl2-draw.html --samples 5 --duration-ms 1000 --out reports/webgl2.json

--file targets are served through a temporary localhost server, so module imports and secure-context APIs behave like a normal local app. Use --file-root DIR when the benchmark imports files outside the benchmark directory.

Run a report against an app URL and include a Chrome trace summary:

node ./src/cli.js run --url http://localhost:5173/bench.html --samples 10 --duration-ms 2000 --trace --out reports/candidate.json

Save the raw Chrome trace too:

node ./src/cli.js run --url http://localhost:5173/bench.html --trace --raw-trace --out reports/candidate.json

For agent loops, launch Chrome once and send repeated jobs to the local server:

node ./src/cli.js serve --port 9099

Then call it from any Node tool:

const response = await fetch("http://127.0.0.1:9099/run", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({
    url: "http://localhost:5173/bench.html",
    samples: 5,
    durationMs: 1000
  })
});
const report = await response.json();

Compare two reports and fail with exit code 1 when a metric regresses beyond the threshold:

node ./src/cli.js compare --base reports/base.json --candidate reports/candidate.json --threshold 5

Both run and compare accept --json for machine-readable stdout — ideal for agents and CI scripts.

Record an Xcode Instruments trace on macOS:

node ./src/cli.js xctrace --url http://localhost:5173/bench.html --template "Metal System Trace" --time-limit 15s --out reports/metal.trace

Report Structure

Every report (file, --json stdout, serve responses, and Node API results) contains a top-level agent-friendly digest:

{
  "summary": {
    "verdict": "excellent",          // excellent | good | needs-work | poor
    "fps": 60,
    "frameTimeMsMean": 16.4,
    "frameTimeMsP95": 17.1,
    "gpuFrameMsMean": 4.2,           // when GPU timing hooks are active
    "jsHeapGrowthMBPerSec": 0.02,
    "trackedVram": { "totalMiB": 128.5, "textureMiB": 96.2, "bufferMiB": 32.3, "textureCount": 14, "bufferCount": 42, "leakedResources": 0 },
    "slowFrameCount": 0,
    "warnings": [ /* actionable bottleneck descriptions */ ]
  },
  "diagnostics": { "warnings": [], "highlights": [], "resources": {} },
  "inPage": { /* per-sample measurements, slow-frame op counts */ },
  "trace": { /* Chrome trace summary when --trace */ },
Record an Xcode Instruments trace on macOS:

```bash
node ./src/cli.js xctrace --url http://localhost:5173/bench.html --template "Metal System Trace" --time-limit 15s --out reports/metal.trace

Node API

import { runReport, compareReports } from "gpu-perf-agent";

const report = await runReport({
  url: "http://localhost:5173/bench.html",
  samples: 10,
  durationMs: 1000,
  trace: true
});

For repeated runs inside one Node process, keep Chrome alive:

import { FastCDPHarness } from "gpu-perf-agent";

const harness = await FastCDPHarness.launch({ channel: "chrome" });
try {
  const before = await harness.run({ url: "http://localhost:5173/bench.html" });
  const after = await harness.run({ url: "http://localhost:5173/bench.html" });
} finally {
  await harness.close();
}

Page Hook

For meaningful GPU work, expose a hook in the page. The runner will call it for warmup and sample phases.

globalThis.__gpuReportBench = async ({ phase, sampleIndex, durationMs, api }) => {
  // Run your workload for roughly durationMs.
  return {
    api,
    fps: 60,
    gpuMs: 4.2,
    allocatedBytes: globalThis.__gpuMemoryTracker?.snapshot().totalBytes
  };
};

Without the hook, the runner still records capability, memory, and requestAnimationFrame timing, but it cannot know which GPU workload you intended to measure.

Declared GPU Allocation Tracking

Use the browser helper to track resources your app creates. This is not exact VRAM residency; it is a deterministic estimate from API descriptors, which is ideal for regression checks.

import { trackWebGPUDevice } from "gpu-perf-agent/browser/allocation-tracker";

const device = await adapter.requestDevice();
globalThis.__gpuMemoryTracker = trackWebGPUDevice(device);

For WebGL2:

import { trackWebGL2Context } from "gpu-perf-agent/browser/allocation-tracker";

const gl = canvas.getContext("webgl2");
globalThis.__gpuMemoryTracker = trackWebGL2Context(gl);

Precise GPU Timing

For precise GPU command timing, instrument the benchmark page. WebGPU timestamp query results are nanoseconds, but the exact timestamp source is implementation-defined by the browser/GPU stack. The runner records these as custom metrics, so compare can gate on them.

import { requestTimedWebGPUDevice } from "gpu-perf-agent/browser/timing";

const adapter = await navigator.gpu.requestAdapter();
const { device, timer } = await requestTimedWebGPUDevice(adapter);

const gpuTimeNs = await timer.measure((encoder, gpuTimer) => {
  const pass = gpuTimer.beginComputePass(encoder);
  pass.dispatchWorkgroups(128);
  pass.end();
});

For WebGL2, use EXT_disjoint_timer_query_webgl2 when available:

import { createWebGL2Timer } from "gpu-perf-agent/browser/timing";

const timer = createWebGL2Timer(gl);
const gpuTimeNs = timer ? await timer.measure(() => draw()) : null;

Suggested Stack

  1. Use the allocation tracker in benchmarks to catch declared buffer/texture growth.
  2. Use gpu-perf-agent run --trace for repeatable browser traces and CDP metrics.
  3. Use gpu-perf-agent compare in CI or agent loops to catch regressions.
  4. Use gpu-perf-agent xctrace when Chrome-level signals are not enough and you need Metal/GPU-driver level evidence on macOS.

License

MIT