three-kit

Swaps

Each swap: what goes in, what comes out, what to keep from the copy. Loaded by steps 2–5. The packages’ READMEs win on signatures.

Budgets

  1. Measure today. For each kind of part the tests build (a cube, a piece, a building), its triangles as the app builds it: three-audit’s countTriangles(object) in a throwaway test, or census() in the running app. The frame’s draws and triangles at each play position come from the baseline (ledger()).
  2. Write src/budgets.ts with defineBudgets from @zkmake/three-audit/budgets, the entry that holds it alone. The app’s own code imports this file (the HUD reads it), and importing from the main entry would put every check in the first load and stop the dev tools’ lazy import of three-audit from splitting off:
    • frame: fps, calls, triangles from the style guide’s Budgets. The numbers in the copies’ BUDGETS literals came from there; where the two disagree, ask the user which holds.
    • parts: one entry per kind, at today’s count plus the headroom the style guide allows (round up to a readable number). Never below today: this pass changes no geometry.
  3. Tests. Import @zkmake/three-audit/vitest once (a setup file or the test), then hold each part: expect(part).toFitBudget(budgets.parts.<kind>). Replace the hand-written counter and its ceiling with it, and add toHaveCleanGeometry() and toHaveNoZFighting() where the test builds geometry. Delete the counter once nothing uses it.
  4. Style guide. Its Budgets section says the numbers live in src/budgets.ts and keeps only the reasons; a number restated in prose is a copy too.

The same file feeds the HUD (step 3) and three-audit’s CLI (three-audit --budgets src/budgets.ts), so the frame target lives in one place.

Dev tools

Install @zkmake/three-devtools as a regular dependency (the app’s code imports it; ?debug works on the deployed build). It brings three-meter and three-audit; an app that imports neither elsewhere drops its direct dependencies on them in step 6.

React Three Fiber, inside the game’s <Canvas>, once:

<DevTools
  budgets={budgets}
  quality={qualityStore}
  dev={import.meta.env.DEV}
  hud={{ defaultPlacement: { edge: "top", align: "end" }, storageKey: "<app>:perf-hud" }}
/>

Vanilla: const tools = mountDevTools({ renderer, scene, budgets, quality, dev }) after the renderer and scene exist, and renderer.setAnimationLoop(tools.wrap(loop)) (or tools.frame() once a frame, before rendering).

Studio

The page stays where it is (mockups.html or studio.html, never built), and so do the area and variant files. Only the shell changes, onto @zkmake/three-studio; follow the scene-mockups skill’s ../../scene-mockups/references/mockup-page.md for a mockups page and the model-pass skill’s ../../model-pass/references/studio.md for a model studio.

Quality

Install @zkmake/three-quality as a regular dependency.

More of kit-adopt