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

@zkmake/three-devtools

v0.2.0

Published

One drop-in for a three.js app's dev tools behind ?debug: the three-meter HUD with the app's budgets and adaptive quality, three-audit's console helpers, and the camera and texture panels on request. Lazy, so a normal load pays nothing. Vanilla three and

Readme

@zkmake/three-devtools

An app's dev tools behind ?debug, in one drop-in: the three-meter HUD with the app's budgets file and its adaptive-quality line, three-audit's console helpers (tris(), ledger(), zfight() …) on the live scene, and the camera and texture panels when the app asks for them. Everything heavy loads through import() only when it's on, so a normal load runs one URL check and stops: three-meter and three-audit end up in chunks the page never fetches. Vanilla three or React Three Fiber, WebGL or WebGPU.

bun add @zkmake/three-devtools   # or npm i / pnpm add

Install it as a normal dependency: the app's code imports it, and ?debug works on the deployed game too. It brings three-meter and three-audit; the panels are separate packages, installed only by the apps that show them.

Entries: @zkmake/three-devtools (vanilla) and @zkmake/three-devtools/react (needs react and @react-three/fiber).

React Three Fiber

import { DevTools } from "@zkmake/three-devtools/react";
import { qualityStore } from "@zkmake/three-quality/react";

import budgets from "./budgets.ts"; // defineBudgets from "@zkmake/three-audit/budgets"

<Canvas>
  <Scene />
  <DevTools budgets={budgets} quality={qualityStore} dev={import.meta.env.DEV} />
</Canvas>;

One component, inside the <Canvas>: it reads the scene and renderer from the canvas and marks a frame for the meter from useFrame. The HUD and the panels are DOM it mounts itself, so there's no second component outside the canvas.

Vanilla three

import { mountDevTools } from "@zkmake/three-devtools";

const tools = mountDevTools({ renderer, scene, budgets, dev: import.meta.env.DEV });

renderer.setAnimationLoop(
  tools.wrap((time) => {
    update(time);
    renderer.render(scene, camera);
  }),
);
// later: tools.dispose()

wrap makes each tick one frame for the meter (or call tools.frame() once a frame, before it renders). Without the flag, and until the meter has loaded, both are pass-throughs.

For GPU time on WebGPU, construct the renderer with trackTimestamp: true, as three-meter needs.

What turns on when

| | Normal load | dev | ?debug | | ----------------------------- | ----------- | ----- | -------- | | three-audit's console helpers | | ✓ | ✓ | | three-meter HUD | | | ✓ | | Panels (panels) | | | ✓ |

The HUD's footer and report list the React and React Three Fiber versions beside three's: <DevTools> loads them behind the flag, and mountDevTools takes the same as frameworks, an async function, for a host that wires React itself.

dev is for the build to say: pass import.meta.env.DEV (Vite) or your bundler's equivalent, so a production build keeps the helpers behind the flag. flag renames the query parameter; enabled overrides it (a settings toggle, a test, an app whose switch is already #debug: enabled: location.hash.includes("debug")). hasDebugFlag(flag?) is the same check, for the app's own debug-only bits.

Budgets and quality

budgets takes the app's budgets file, three-audit's defineBudgets, whose frame sets the HUD's target frame rate, draw calls and triangles; the same file the app's tests hold parts to with toFitBudget. Write it with defineBudgets from @zkmake/three-audit/budgets, which holds it alone: from the main entry, the app's first load would carry every check and the console helpers couldn't split off. The HUD's own budgets object ({ calls: 60, targetFps: 60 }) works too.

quality shows three-quality's state as one line in the HUD, dpr 1.25 · adapting: its qualityStore in React, the adaptQuality(…) handle in vanilla. It's read by shape, so this package doesn't depend on three-quality. Both apply live.

Panels

Each panel is the app's own import() of the panel package, so only an app that installs a panel bundles it, and only ?debug fetches it:

<DevTools
  budgets={budgets}
  panels={{
    cameras: () => import("@zkmake/three-cameras/ui"),
    textures: () => import("@zkmake/three-textures/ui"),
  }}
/>

They mount with the canvas's scene and renderer, the theme, and invalidate (for frameloop="demand"). A panel that fails to load warns and leaves the rest.

Keep it out of the first load

The tools only stay lazy if the bundler gives them their own chunks. Two things undo that:

  • A catch-all vendor chunk group: a rule that sends every node_modules module to one vendor chunk (Vite's codeSplitting.groups, manualChunks) puts three-meter and three-audit in a chunk index.html preloads. Leave @zkmake out of it:

    // vite.config.ts, build.rolldownOptions.output.codeSplitting.groups
    {
      name: "vendor",
      // Everything in node_modules but @zkmake, whose dev tools load behind ?debug.
      test: /^(?!.*[\\/]node_modules[\\/]@zkmake[\\/]).*[\\/]node_modules[\\/]/,
      priority: 10,
    },
  • The budgets file from three-audit's main entry: the app's code imports it, so import defineBudgets from @zkmake/three-audit/budgets, as above.

Check a production build: no chunk that index.html loads or preloads contains three-meter's PerformanceMonitor or three-audit's findZFighting (not a storage key like perf-hud, which the app's own code holds), and a load without the flag fetches neither.

Options

mountDevTools(options) and <DevTools> (all but renderer, scene and invalidate, which it reads from the canvas) take:

| Option | Default | Meaning | | ------------ | --------- | ---------------------------------------------------------------------------------------------------- | | renderer | required | The app's renderer. | | scene | required | The app's scene: the helpers and panels read it. | | flag | "debug" | The query parameter that turns the tools on. | | enabled | the flag | On or off whatever the URL says. | | dev | false | The console helpers without the flag. | | budgets | | The app's budgets file, or the HUD's own budgets; false for none. | | quality | | three-quality's qualityStore or adaptQuality handle. | | theme | system | dark, light or system, for the HUD and the panels. | | hud | | three-meter's mountPerfHud options (defaultPlacement, storageKey, mode); false for no HUD. | | audit | | three-audit's installAuditHelpers options (skip, bakes, globals); false for none. | | panels | none | { cameras, textures }: loaders, as above. | | invalidate | | Ask for a frame, for render-on-demand loops. |

The handle: enabled, ready (settles once everything that's on has mounted), monitor and hud once loaded, frame(), wrap(loop), setBudgets, setQuality, dispose().

In React, budgets (by value) and quality apply live; flag, enabled, dev, hud and which panels are set remount the tools; the rest is read on mount.

Origin: the debug-tools.tsx and perf-meter.tsx pair copied into edclub's games, one per game, each with its budgets written into the file.

License

MIT