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

sloptimize

v0.4.8

Published

The agent-native profiler for three.js games: an always-on flight recorder, per-entity cost attribution, and a deterministic bench — so a coding agent can measure, attribute, and verify instead of guessing.

Readme

sloptimize

npm

sloptimize optimizes your game's rendering performance by finding the bottlenecks and reporting them to Claude Code to fix — all while you just play the game. No action is required on your end.

The agent-native profiler for browser games. Your coding agent cannot watch a game run — it will never feel a hitch, cannot screenshot 60 times a second, and cannot verify a "fix" it cannot measure. sloptimize gives the agent the three verbs it measurably lacks:

  • MEASURE — an always-on flight recorder detects incidents (CPU spikes, fps drops, GPU stalls, and the player's unit or camera SNAPPING off its own trajectory) automatically, in the background, and writes them to disk before anyone asks. The human just plays.
  • ATTRIBUTE — every incident arrives classified with evidence (shader-compile: programs +2, long-script, upload storms, snap 17.5m in one frame), stamped with a footprint — the identity of its cause and the game's situation (which machine, at the helm or on foot, in combat…), never its time — so one cause across builds, sessions and players is one issue with a count and a fix history, and — with the attach tier — named by function and file:line from a rolling sampling profiler.
  • VERIFY — exact counters (draw calls, triangles, pipelines — deterministic on any renderer), perf budgets with exit codes, and honest labels: timing numbers carry their regime (hardware/software) and are never compared across them.

The division of labor is the design: the tool decides what is true; the agent decides what to try; the human plays.

Proven in production on a 149k-line WebGPU battle-royale: the pipeline caught a 205,000-calls/11s GPU upload storm from a player's real session, attributed it, and verified the fix at >60× reduction — with the player doing nothing but playing.

The pipeline

game/browser ──► incidents (auto-detected, classified, clustered)
                    │
                    ▼
              .sloptimize/          ◄── the agent's reading room
              profile.json            rolling summary (median/p95/counters/regime)
              perf.jsonl              incident records, append-only, each with its footprint
              clusters.json           one cause = one cluster
              census.json             per-entity cost census (tier 1+)
              fixes.jsonl             the fix ledger: issue → solution, commit, MEASURED before/after
              budgets.json            YOUR limits (the one human-authored file)
                    │
        ┌───────────┴───────────┐
        ▼                       ▼
   Claude Code (agent)     in-game debugger (human, OPTIONAL)
   woken on new incidents   Session · Issues · Optimizations · Settings —
   (fp=<id> ×N on each);    this tab's incidents + a note box; every cause
   reads, fixes, verifies,  grouped by footprint with ×N, last seen and the
   records each fix         fixes applied; p95/calls/hitches over time

Showing the work is part of the loop: after a verified fix the agent runs sloptimize fix --title … --issue … --solution … --commit <sha>, and the record's before/after are two measured windows of the ledger (previous build vs new build) — not numbers the agent typed. The debugger's Fixes tab and sloptimize history read that ledger back.

Install

Published on npm as sloptimize. Node 22+.

npm i -D sloptimize          # in your game repo (recommended)
npx sloptimize --version     # confirm: prints the installed version

No install at all for a one-off: npx sloptimize attach --launch http://localhost:3000. Working from a git checkout instead? node /path/to/sloptimize/bin/sloptimize.mjs … runs on bare Node.

Zero dependencies, no postinstall, no supply chain — npm is delivery only.

Quickest start: zero integration (tier 0)

Requires only Node 22+ and a Chromium. No game changes, no build changes:

npx sloptimize attach --launch http://localhost:3000 --headless
# play / drive the game …then:
npx sloptimize report

Attach connects over the Chrome DevTools Protocol, injects a recorder before any page script (rAF timing, draw/triangle counts via graphics-API wraps, pipeline creations WITH call stacks, upload bytes, GPU queue latency), and runs a rolling sampling profiler so a freeze is attributed like:

INCIDENT long-script|[email protected]:512 — 900ms

Limits, stated: Chromium-only; minified bundles attribute to minified names unless you serve sourcemaps; entity-level attribution needs tier 1+.

Higher fidelity: the in-page feed (tier 1)

One call per frame from wherever your loop already reads renderer.info:

import { createRecorder } from 'sloptimize';
const rec = createRecorder({ budgetFrameMs: 16.7 });
// per frame:
rec.frame({ frameMs, insideRenderMs, calls, triangles, programs,
            geometries, textures, spawned, paused });
// optional human channel (bind to a chord, e.g. Ctrl+F11):
rec.usermark({ windowMs: 5000, note, inputsHeld, world });

Ship the records to .sloptimize/ however your stack likes — a vite host gets a plugin (planned); any other host adds one dev-gated POST endpoint (~100 lines; see docs/INTEGRATION.md for the reference implementation, including the four traps that cost the first deployment real time: don't gate activation on hostname (probe your dev endpoint instead), give the recorder its own rAF clock (a game-loop-fed clock is blind to boot/launch — exactly the windows you care about), frameMs must bound insideRenderMs, and never let the feed die silently (retry the probe and the posts on a backoff, buffer while dark, and SHOW the state — the first deployment lost an hour of real freezes to a server restart that dropped the ingest with no indication anywhere).

The wire contract the reference runtime keeps, so the files are useful on their own:

  • every record is self-sufficientbuild (which bundle the tab runs) and phase (menu/boot/launch/match…) ride each ledger line; hitches are stamped at mint time, not post time;
  • a heartbeat record lands once a minute while armed, so a quiet perf.jsonl means "no session, or the feed is dark" — never just "idle" (sloptimize hook-status warns when the ledger goes stale);
  • gpu-settle records report how long a boot/reveal gate actually waited on onSubmittedWorkDone — the on-hardware verification channel for compile-stall fixes;
  • a hitch that overlapped pipeline/shader creates carries createStacks — the top 3 deduped Error().stack tails from the create wrappers (~2KB cap), so a programs +N hitch from a machine you cannot profile names its own call sites. The positions are minified (bundle.js:L:C); keep an unreferenced sourcemap at build time and decode locally (the game repo's tools/decode-perf-stack.mjs is a dependency-free reference decoder).

Coordinate jitter and the issue catalogue (tier 1, ~200 lines in the game): feed the unit's and the camera's positions once per rendered frame and a snap or oscillation lands as a classified jitter record; declare a few facets of the player's situation and every incident of every kind is footprinted, counted and linked to its fixes — the debugger's Issues tab, sloptimize issues, and fp=<id> ×N on every wake line. The whole recipe, with the three traps that make a naive position detector lie (rotation, transient shakes, the sim's dt clamp), is docs/JITTER-AND-FOOTPRINTS.md.

import { createMotionMonitor, canonicalContext, footprintOf } from 'sloptimize';
const motion = createMotionMonitor({ unit: 'm', longFrameMs: 50,
  tracks: { unit: { floor: 0.1 }, camera: { floor: 0.1, reach: 'boom', follows: 'unit' } } });
// per rendered frame, after the render:
motion.sample('unit',   pivot.x, pivot.y, pivot.z, now, { held: paused, phase, ctx });
motion.sample('camera', cam.x,   cam.y,   cam.z,   now, { held: paused || lookInput, reach, phase, ctx });
// once a second:  ctx = canonicalContext({ stance: 'helm', hull: 'elong-x', squad: 'duo', combat: 'no' });
// at post:        for (const r of records) { r.ctx ??= ctx; const fp = footprintOf(r); if (fp) r.footprint = fp; }

Tier 2 (scene census, per-entity attribution, measured bisection) layers on top where the engine grants scene access — see docs/SPEC.md §4.

Claude Code integration — the whole point

The npm package IS a Claude Code plugin — skill, prompt hook, and MCP server ship inside it. After npm i -D sloptimize, point Claude at it:

claude --plugin-dir node_modules/sloptimize

Alternatives: claude --plugin-dir /path/to/sloptimize from a git checkout, or via the marketplace: /plugin marketplace add m0dE/sloptimize then /plugin install sloptimize.

Then let the agent wire your game: /sloptimize:install walks it through the tier-1 integration (runtime, sink, budgets, hooks) and refuses to call itself done until the feed is proven live end-to-end.

That carries three surfaces into every session:

  • Skill — the doctrine: read → classify → census → ONE change → verify with counters; never claim a perf fix without a measured before/after; never quote timing from a software regime.
  • Prompt hook — silent by default; when a NEW keyframe or budget breach exists, up to five lines land in the agent's context on your next prompt.
  • MCP serverget_report, check_budgets, get_history, get_issues (the catalogue by footprint), record_fix (with the footprints it addresses), and attach_start / attach_stop for the live tier.

For instant wakeups (the agent starts fixing ~20s after the stutter, no prompt needed), arm sloptimize watch as a session Monitor — one line, in docs/INTEGRATION.md §5. Wire it into a SessionStart hook and every session arms it by itself.

Cloud — sloptimizejs.com

Everything above is local: one machine's .sloptimize/ directory, read by that machine's shell and that machine's Claude Code session. sloptimize cloud widens the same catalogue to every player, every build — not just the one in front of you: an issues catalogue across 24h/7d/30d or any range, a timeline of p95 frame time, draw calls and incidents across all tabs with build boundaries and fix markers, one page per player session, and every fix measured against what players saw. Client incidents and server incidents (sloptimize/node) fold into the same footprint identity; uncaught errors (createErrorMonitor) are their own incident kind. The local product stays the default story — nothing below changes what a project with no cloud key does.

Getting on it takes three steps:

  1. Sign in at sloptimizejs.com with GitHub. The free plan (5,000 incidents a month, 7 days of raw records, one project) needs no card; Pro and Team lift the limits.
  2. Create a project and open its settings page: it holds the two keys and the same three snippets below, filled in.
  3. Paste the snippets. The endpoint is https://sloptimizejs.com/v1/ingest for both sinks; the CLI and MCP take the base, SLOPTIMIZE_ENDPOINT=https://sloptimizejs.com.
// browser: the cloud sink is a TEE beside your existing drain, never instead
// of it — errors ride the same recorder as hitches, so one drain feeds both
import { createRecorder, createErrorMonitor, createCloudSink } from 'sloptimize';
const rec = createRecorder({ budgetFrameMs: 16.7 });
createErrorMonitor(rec);
const cloud = createCloudSink({ key: 'pk_live_…', endpoint: 'https://sloptimizejs.com/v1/ingest', build });
// in the ~2s drain you already have (docs/INTEGRATION.md §1):
const batch = rec.drainRecords();
post('records', batch);   // unchanged: .sloptimize/perf.jsonl, still the source of truth
cloud.enqueue(batch);     // the same records, teed to the cloud sink's own queue

(The sources: [rec] option exists only for a host with no file sink at all: the sink drains those sources itself, so anything it takes never reaches your own drainRecords().)

// game server (Node): ticks, event-loop stalls, and uncaught errors
import { createServerRuntime } from 'sloptimize/node';
const server = createServerRuntime({ key: process.env.SLOPTIMIZE_KEY, endpoint: 'https://sloptimizejs.com/v1/ingest', build, tickBudgetMs: 16 });
server.tick(() => stepWorld());   // a tick over budget is a server-hitch, attributed by the V8 sampler

The server runtime registers uncaughtExceptionMonitor only, so it observes a crash without ever becoming part of the crash path. One consequence worth knowing: under --unhandled-rejections=warn or none, unhandled rejections are not captured — that event sees them only in Node's default throw mode, and listening to unhandledRejection instead would suppress the throw your process relies on.

# CLI: read the cloud catalogue instead of this machine's ledger
export SLOPTIMIZE_KEY=sk_live_… SLOPTIMIZE_ENDPOINT=https://sloptimizejs.com
npx sloptimize issues --cloud --preset 7d
npx sloptimize fix --title "…" --push   # records locally, then pushes

Honesty is the whole pitch: a dropped-locally count rides every batch the sink sends, so the dashboard's numbers say what they could not see rather than pretending nothing was lost.

Two kinds of key, and the difference matters. The publishable key is public and write-only (it can post incidents, never read anyone else's), so shipping it in a client bundle is the intended use, not a leak — that is the key in the browser snippet above. The secret key is the one the server runtime, the CLI (SLOPTIMIZE_KEY) and the MCP server use: it reads your whole catalogue (/v1/issues) and writes fixes (/v1/fixes). A secret key never goes in a client bundle.

Budgets: "fast enough" as an exit code

.sloptimize/budgets.json (the one file a human reviews):

{ "perf.budget.draw_calls": 400, "perf.budget.frame_ms_p95": 16.7 }
npx sloptimize check              # exit 0 inside · 1 breached · 4 unmeasured

That exit code is what lets an agent self-iterate in a loop that terminates.

CLI

sloptimize report        current profile + incidents + census hints
sloptimize check         budgets → exit code (--counters-only for CI)
sloptimize census        per-entity costs + closed-vocabulary hints
sloptimize history       the timeline: p95 / draw calls / hitches per time
                         bucket and per build, plus the fix ledger
sloptimize fix           record a verified fix (title, issue, solution,
                         commit) with MEASURED before/after windows
sloptimize attach        tier-0: --launch <url> [--headless] [--port N]
sloptimize hook-status   the prompt hook's ≤5-line ambient surface
sloptimize issues        the catalogue: every incident grouped by FOOTPRINT
                         (cause + situation, never time) — how often, how
                         recently, which fixes were applied; --fp <id> for one
sloptimize watch         the push channel: one stdout line per usermark /
                         ≥100ms hitch / gpu cap-hit / coordinate jitter /
                         feed dark, each with fp=<id> ×N; never exits
sloptimize doctor        what is wired, what is degraded, stated limits

What it will tell you it cannot do

Printed by doctor, kept in the spec, never silently degraded: no per-draw GPU timing; bisection ranks rather than sums; workload repro, not trajectory repro; timing from software renderers flagged and never compared; V8 inlining can split an incident cluster across an optimization boundary; correctness bugs are out of scope — a profiler cannot find a logic bug, and the doctrine routes "it looks/behaves wrong" reports away before anyone burns a loop on them.

Docs

  • docs/USAGE.md — day-to-day use once wired: the operator's verbs, new-session pickup, multi-session semantics, monitoring options
  • docs/SPEC.md — the founding specification (recorder, census, bench, anti-gaming posture)
  • docs/SPEC-attach.md — v2: the incident pipeline, tier-0 attach, measured exit criteria
  • docs/INTEGRATION.md — wiring a real game + Claude Code session, with the reference deployment's traps
  • docs/DESIGN-mecharoyale-v0.md — the first field deployment's decision record

Status

M0–M2 (recorder, census, budgets/CLI) and M-A0–A2 (attach, incident identity, plugin packaging) shipped with measured exit criteria. Bench + correctness gate (SPEC §6, M3) and paused-world bisection (M4) are next.

Relationship to slopjs

A sibling on the same platform: slopjs is a pointing device for a human-in-the-loop authoring session; sloptimize is a measurement loop that works with nobody watching. Tier 2 consumes @slopjs/inspector primitives (stable IDs, the coherent pause, snapshots) where present.

License

MIT