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

ushman-threejs-tools

v0.4.0

Published

Three.js-aware helpers for the ushman ecosystem. Scene-tree canonicalization, brand checks, runtime preboot constants, inspector driver. No CLI.

Downloads

146

Readme

ushman-threejs-tools

Three.js-aware helpers for the ushman ecosystem. Scene-tree canonicalization, scene-tree diff, runtime preboot constants, the Vector3 / Quaternion / Object3D brand-checks used by the trace serializer, and the Puppeteer page-script driver pieces that inspect a running Three.js scene.

This is a library package — no CLI. Other ushman packages (@ushman/characterize, @ushman/spector) and the main ushman orchestrator's parity/ consume it.

Status

Extracted from ushman/src/adapters/web-app/sub-adapters/scene-heavy/ and the former ushman/src/tools/** tree, with the shared Three.js surface now published here. Runtime: mixed — pure Node for canonicalizers and constants; browser-side for the inspector page-script (loaded via Puppeteer).

What this package is NOT

  • Not a Three.js fork or wrapper. We use Three's public API; we don't reimplement Mesh / Material / Renderer.
  • Not a parity verdict. ushman parity owns that.
  • Not a characterization runner. @ushman/characterize owns trace capture; we just expose the brand-checks and canonicalizer it needs.

Install

npm i ushman-threejs-tools
bun add ushman-threejs-tools

three is a peerDependency; consumers vendor their own.

Public API

// Canonicalization (Node-side, used by characterize + parity diff)
export const canonicalizeSceneTree: (value: unknown, opts?: { exemptFields?: readonly string[]; precision?: number }) => unknown;
export const diffSceneTrees: (a: unknown, b: unknown, opts?: { exemptFields?: readonly string[]; precision?: number }) => readonly { path: string; verdict: 'add' | 'remove' | 'change'; details?: unknown }[];
export type SceneTreeDiff = { path: string; verdict: 'add' | 'remove' | 'change'; details?: unknown };

// Three.js brand-check primitives (Node-side, used by trace serializers)
export const isVector3: (v: unknown) => boolean;
export const isQuaternion: (v: unknown) => boolean;
export const isMatrix4: (v: unknown) => boolean;
export const isObject3D: (v: unknown) => boolean;
export const isBufferGeometry: (v: unknown) => boolean;
export const isMaterial: (v: unknown) => boolean;
export const isTexture: (v: unknown) => boolean;
export const stableObject3DUuid: (obj: unknown) => string; // deterministic scene identity hash, not an RFC UUID

// Inspector driver (browser-side, page-script)
export const sceneInspectorPageScriptSource: string;   // injectable via Puppeteer.evaluate
export const captureSceneGraph: (
  page: unknown,
  opts?: {
    timeoutMs?: number;
    pollIntervalMs?: number;
    onTimeoutDiagnostics?: (diagnostics: RuntimeReadyTimeoutDiagnostics) => void | Promise<void>;
  }
) => Promise<SceneGraphSnapshot>;
export const captureSceneSnapshot: (page: { evaluate: <T>(source: string) => Promise<T> }) => Promise<{
  capturedAt: string;
  extraction: unknown;
  sceneState: unknown;
  domProbe: unknown;
  warnings: string[];
}>;
export const waitForRuntimeReady: (
  page: unknown,
  opts?: {
    timeoutMs?: number;
    pollIntervalMs?: number;
    onTimeoutDiagnostics?: (diagnostics: RuntimeReadyTimeoutDiagnostics) => void | Promise<void>;
  }
) => Promise<boolean>;

// Runtime preboot (browser-side; constants used by capture/sweep)
export const RUNTIME_PREBOOT_HOOK_SOURCE: string;
export const RUNTIME_PREBOOT_HOOK_VERSION: string;
export type RuntimePublicationMetadata = {
  importMapPresent: boolean;
  moduleResolution: 'bundler' | 'unknown';
  publishedAtFrame: number;
  score: number;
  source: string;
  sourcePath: string | null;
  syntheticApp: boolean;
};

// SceneGraphSnapshot is the combined payload returned after the driver waits for preboot readiness.
export type SceneGraphSnapshot = {
  capturedAt: string;
  domProbe: unknown;
  extraction: unknown;
  readyViaPreboot: boolean;
  sceneState: unknown;
  warnings: string[];
};

export type RuntimeReadyTimeoutDiagnostics = {
  capturedAt: string;
  diagnosticError: string | null;
  documentReadyState: string | null;
  href: string | null;
  prebootDebug: unknown;
  prebootState: {
    captureStatePresent: boolean;
    debugHelperPresent: boolean;
    globalRootsPresent: boolean;
  };
  rafFrameCount: number;
  runtimeRoots: {
    capture: { app: boolean; metadata: boolean; waterSystem: boolean };
    global: { app: boolean; metadata: boolean; waterSystem: boolean };
  };
  warnings: unknown[];
};

// Diagnostics are best-effort: `prebootDebug` is only populated when the
// preboot hook injected and executed successfully enough to publish its debug
// helper before readiness timed out.

// ushman v4 scene-heavy evidence helpers (Node-side; v4 = the ushman workspace/evidence contract)
export const createThreejsRunId: (value?: Date | string) => string;
export type ThreejsBootMode = 'vite-preview' | 'vite-dev' | 'unknown';
export const resolveThreejsEvidencePaths: (workspaceRoot: string, runId: string) => {
  rootDir: string;
  runDir: string;
  capturePath: string;
  diagnosticsPath: string;
  prebootTelemetryPath: string;
  manifestPath: string;
};
export const writeThreejsEvidenceBundle: (input: {
  workspaceRoot: string;
  runId: string;
  capture: SceneGraphSnapshot;
  bootMode?: 'vite-preview' | 'vite-dev' | 'unknown';
  gpuDiagnostics?: unknown;
  prebootTelemetry?: unknown;
}) => Promise<{
  manifest: {
    schema: 'threejs-evidence/v1';
    runId: string;
    bootMode: 'vite-preview' | 'vite-dev' | 'unknown';
  };
  paths: ReturnType<typeof resolveThreejsEvidencePaths>;
}>;
export const buildThreejsCaptureLedgerPayloads: (input: {
  runId: string;
  capture: SceneGraphSnapshot;
  bootMode?: 'vite-preview' | 'vite-dev' | 'unknown';
  resultPath?: string;
}) => {
  toolInvocation: { summary: string; details: Record<string, unknown> };
  validatorResult: { verdict: 'green' | 'yellow' | 'red'; summary: string; metrics: Record<string, unknown> };
};

SceneGraphSnapshot example:

const snapshot: SceneGraphSnapshot = {
  capturedAt: '2026-05-13T18:22:00Z',
  domProbe: { selectedPresetValue: 'lobby' },
  extraction: {
    runtimeAccess: {
      cameraPath: 'window.__app.camera',
      rendererPath: 'window.__app.renderer',
      scenePath: 'window.__app.scene',
    },
    sceneGraph: {
      totalTriangles: 128,
    },
  },
  readyViaPreboot: true,
  sceneState: {
    totalTriangles: 128,
  },
  warnings: [],
};

readyViaPreboot is false when readiness timed out before capture. The returned payload may still contain partial extraction data, but callers should treat it as degraded and inspect warnings plus any timeout diagnostics before producing parity or characterization verdicts.

Example timeout diagnostics usage:

const snapshot = await captureSceneGraph(page, {
  timeoutMs: 5_000,
  pollIntervalMs: 50,
  onTimeoutDiagnostics: (diagnostics) => {
    if (!diagnostics.prebootState.captureStatePresent) {
      console.warn('Preboot never surfaced before timeout:', diagnostics);
    }
  },
});

Subpath entries expose the lower-level helpers used by peers:

  • ushman-threejs-tools/inspector
    • captureSceneGraph, captureSceneSnapshot, installSceneHeavyPreboot, readPrebootHookSource
    • captureChromiumGpuDiagnostics, captureFromPage, collectInterestingGpuLines, extractGraphicsFeatureStatus, summarizeSystemInfo
    • createThreejsRunId, resolveThreejsEvidencePaths, writeThreejsEvidenceBundle, buildThreejsCaptureLedgerPayloads
    • EXTRACTION_SCRIPT, READ_SCENE_STATE_SCRIPT, DOM_RUNTIME_PROBE_SCRIPT
    • composeSceneObjectExportExpression, composeSceneObjectVisibilityExpression
  • ushman-threejs-tools/preboot
    • CAPTURE_THREE_TREE_KEY, RUNTIME_ROOTS_KEY, RuntimePublicationMetadata, RuntimeRoots
    • RUNTIME_PREBOOT_HOOK_USER_SCRIPT

Why a separate package

  1. Three.js peer dependency. Pinning three in the orchestrator forces every other ushman package to pin the same version. Pulling Three.js consumers into one package contains the choice.
  2. Shared by characterize + spector + parity. Without this package, all three would re-implement canonicalizeSceneTree. That's how round-4 lessons get re-learned.
  3. Browser/Node split. The inspector page-script is real browser JavaScript; the canonicalizer is Node. Keeping both behind one entry-point with explicit subpath imports (ushman-threejs-tools/inspector, ushman-threejs-tools/canonical) makes the boundary explicit.
  4. Graphics-only. This package is for Three.js / WebGL / WebGPU donors. Browser-extension adapters, generic non-graphical web apps, and embedded webviews do not depend on it. Keeping it isolated means non-graphical work doesn't pay a Three.js peer-dep cost.

Where this fits in the family

| | | |---|---| | Peer-depended-on by | @ushman/characterize, @ushman/spector, ushman (parity) | | Does NOT depend on | any other @ushman/* package | | Has no CLI | this is a library only |

Development

bun run generate:sources
bun run build
bun run typecheck
bun test

generate:sources bundles src/preboot/entry.ts into the generated string constant exported as RUNTIME_PREBOOT_HOOK_SOURCE.

When you change src/preboot/*.ts, rerun bun run generate:sources before bun test or bun run build so src/preboot/generated-source.ts stays in sync.

Browser-backed E2E coverage for the preboot and inspector driver helpers runs against the local fixtures under test/fixtures/preboot/. Set PUPPETEER_EXECUTABLE_PATH when Chrome or Chromium is installed outside the standard locations from test/fixtures/preboot/harness.ts; otherwise those E2E suites skip automatically.

Stability

  • Root exports and documented subpath exports are the stable integration surface and should follow semver.
  • RUNTIME_PREBOOT_HOOK_SOURCE and the emitted threejs-evidence/v1 manifest are intended for downstream automation and should only change compatibly outside a major version.
  • The internal structure of the large injected browser scripts is implementation detail. Consumers should use the exported helpers instead of depending on string internals.

V3 to V4

  • v4 scene-heavy evidence lives under <workspace>/.lab/threejs/<run-id>/ instead of older screenshot- or stage-oriented layouts.
  • Bundler-resolved Vite pages are the supported runtime target. The preboot hook no longer changes behavior based on importmap mode.
  • Callers that own boot orchestration should pass the concrete ThreejsBootMode into writeThreejsEvidenceBundle() and buildThreejsCaptureLedgerPayloads() instead of assuming the helper can infer it.

Runtime Notes

  • ushman v4 here means the version 4 workspace and evidence contract used by the broader ushman pipeline.
  • v4 callers should boot scene-heavy candidates in production shape (vite build && vite preview) and persist capture evidence under <workspace>/.lab/threejs/<run-id>/. This package now exposes reusable path, write, and ledger-summary helpers for that contract.
  • Importmap-backed externalized ESM pages now pre-import three, three/webgpu, and three/tsl through the page's own import map before falling back to scene-host heuristics. When that early path wins, runtime-root metadata reports source: 'bundle-intercept' with importMapPresent: true.
  • The offscreen-worker relay bootstraps through a Blob URL before importing the donor worker script. Apps with a strict worker-src CSP may block that path; the hook degrades back to the original worker when bootstrapping fails, but telemetry coverage will be reduced.
  • stableObject3DUuid is a deterministic scene identity hash for matching comparable nodes across captures. It is stable for equivalent structure, not a unique per-instance UUID.
  • Bundler-resolved Vite pages are the default v4 target. When the hook proves a live scene + renderer + camera triple but the donor app never published one itself, it may synthesize window.__app only when that global is empty or already owned by a prior synthetic publication. The published runtime root is always mirrored to window.__USHMAN_RUNTIME_ROOTS__.app; its metadata follows the exported RuntimePublicationMetadata shape.
  • Injected preboot globals now include window.__debugThreePrebootTelemetry() for lightweight readiness diagnostics and window.__cleanupThreePrebootTelemetry(reason?) for tearing down registered long-lived listeners and wrappers after capture. The debug payload includes the active preboot build version under prebootVersion.
  • When readiness times out, RuntimeReadyTimeoutDiagnostics reports whether the preboot capture state, runtime-roots global, and debug helper were ever observed. That distinguishes "preboot never surfaced" from "preboot surfaced but no runtime roots were published" without changing the main capture contract.
  • Restrictive CSP environments are still diagnostics-first in this release. The driver can now report that the preboot globals never appeared before timeout, but a dedicated same-origin module-bridge fallback for CSP-constrained pages is not implemented yet.

Telemetry Limits

  • Rolling history caps such as MAX_HISTORY and MAX_THREE_SAMPLES keep the newest entries and drop the oldest ones once full.
  • Capture caps such as MAX_UNIFORMS, MAX_PROGRAMS, MAX_SHADERS, MAX_TEXTURES, and MAX_FRAMEBUFFERS stop adding new telemetry records after the limit is reached.
  • Discovery caps such as MAX_NODE_GRAPH_TRAVERSE, MAX_RUNTIME_DISCOVERY_QUEUE_NODES, and MAX_RUNTIME_DISCOVERY_VISITS stop descending once the traversal budget is exhausted.
  • Settled-scene sampling uses a bounded queue walk for non-exhaustive reads. When the walk short-circuits, sceneStats.sampled is true and the reported object/light/mesh counts should be treated as representative lower bounds rather than exact scene totals.

Cleanup Behavior

| Hook or Record | Restored by __cleanupThreePrebootTelemetry() | Notes | |---|---|---| | window.requestAnimationFrame | Yes | Restores the original function reference. | | Load listener installed by the preboot hook | Yes | Removes the tracked listener if it is still registered. | | Canvas context event listeners attached by the preboot hook | Yes | Removes the tracked webglcontext* listeners that the hook installed. | | Importmap observer/listeners installed by the preboot hook | Yes | Disconnects the observer and removes its lifecycle listeners. | | Patched scene.onBeforeRender callbacks | No | Existing donor objects keep the wrapped callbacks already installed. | | Patched material/object render hooks | No | Existing donor objects keep the wrapped callbacks already installed. | | Published runtime roots | No | Cleanup is about teardown of long-lived hook machinery, not unpublishing capture evidence. |