# three-blocks

Build interactive WebGPU scenes with [Three.js](https://threejs.org). Three Blocks adds production-ready materials, simulation, Gaussian splats, text, and visual effects without taking over your scene or render loop.

[Choose a result and follow the shortest working path](https://threejs-blocks.com/docs).

## Install

```sh
npm i three-blocks three
```

`three` is a peer dependency with an open range (`>=0.185.0`): the latest Three.js release is always supported, and each Three Blocks release is verified against the current stable and its next dev build.

## Render a refractive object

Add a Three Blocks material to the Three.js scene you already own:

```ts
import * as THREE from "three/webgpu";
import { MeshTransmissionNodeMaterial } from "three-blocks/transmission";

const material = new MeshTransmissionNodeMaterial({
  color: 0xcde3ff,
  roughness: 0.12,
  thickness: 0.65,
  transmission: 1,
});

scene.add(new THREE.Mesh(new THREE.IcosahedronGeometry(1, 5), material));
```

Initialize `WebGPURenderer` before you create GPU-backed blocks, update block state before `renderer.render()`, and dispose owned resources when the scene leaves. The [first-scene guide](https://threejs-blocks.com/docs/start/first-scene) provides the complete renderer, frame-loop, resize, and cleanup code.

Use the package root for the curated stable facade. Import focused products from their canonical paths:

```ts
import { Boids, ComputeInstanceCulling, MSDFText } from "three-blocks";
import { BakedMotion } from "three-blocks/baked-motion";
import { GaussianSplats } from "three-blocks/gaussian-splats";
```

## Reference

### API compatibility

The curated 22-block catalogue defines the supported package surface. Its machine-readable source of truth is [`api-surface.json`](./api-surface.json).

| Maturity     | Canonical path                           | Compatibility promise                                                                                     |
| ------------ | ---------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| Stable       | `three-blocks` or `three-blocks/<block>` | Incompatible removals, renames, path moves, and signature changes require the appropriate SemVer release. |
| Experimental | `three-blocks/experimental/<block>`      | May change as its product contract matures; changes remain documented and receive a changeset.            |

Symbols not approved by the manifest are internal implementation details and are not package exports. The package root contains only the 17 stable runtime values approved by the manifest.

Experimental paths never appear at the package root. Pin an exact package version when your application depends on an experimental symbol.

### Typed worker assets

`three-blocks/assets` is renderer-agnostic: it provides typed manifests, scheduling,
progress, retries, HMR diffing, and reference-counted lifetime, while applications
inject their own worker-safe renderer and decoder adapters. The standard adapter
contract names GLTF/GLB, Draco/KTX2/Meshopt capabilities, HDR/EXR, textures, JSON,
binary, cube textures, audio, and fonts; it does not pretend to load them without
those concrete adapters.

An entry declared `persistent` stays cached at zero references until explicit
`evict()` or manager `shutdown()`.

Keep the detailed request returned by `load()` and call `release()`, or use the
explicit direct-await lease so ownership cannot be discarded accidentally:

```ts
const lease = await assets.lease(assetManifest);

try {
  useScene(lease.assets.model);
} finally {
  lease.release();
}
```

Hot-replaceable scenes normally receive a scoped facade, which retains direct-await loads
until that scene is disposed:

```ts
const sceneAssets = manager.createScope();
const assets = await sceneAssets.load(assetManifest);

// When the scene is replaced:
sceneAssets.dispose();
```

### DOM-synchronized MSDF text

The text runtime keeps semantic DOM layout and accessibility on the main thread while a
worker renders matching MSDF batches:

```ts
import { defineText } from "three-blocks/text";
import { createTextSync } from "three-blocks/text/main";
import { createTextRenderer } from "three-blocks/text/worker";
```

`createTextSync()` measures browser line boxes and publishes one structured-cloneable batch
per frame. Its complete snapshot can be replayed after worker replacement. The worker owns
a long-lived AssetManager, atlas routing, batched members, glyph diagnostics, retained
readiness, and atomic configuration/atlas replacement.

### Worker-first Vite setup

`three-blocks/vite` emits the exact r185 Draco and Basis runtime files, keeps Three.js out
of the main thread, and derives shader/text build policy from committed receipts. Text
projects pass their static public configuration so production builds can validate atlas
checksums and glyph coverage without a GPU:

```ts
import { defineConfig } from "vite";
import { threeBlocks } from "three-blocks/vite";
import { textConfig } from "./three-blocks.text";

export default defineConfig(({ mode }) => ({
  plugins: [
    threeBlocks({
      shaders: { strict: mode === "strict" },
      text: { configuration: textConfig },
    }),
  ],
}));
```

Development uses safe live shader compilation when a receipt is stale. A normal build
prints the same state in its final receipt; strict mode rejects stale shaders. Text builds
always fail closed on missing or invalid glyph assets. During development, a newly required
glyph keeps its DOM fallback visible while the plugin queues `three-blocks text generate`.

### Precompile shaders in an existing WebGPURenderer project

Three Blocks devtools has one product surface. The runtime face is
`three-blocks/devtools` plus the `three-blocks/vite` plugin; the command face is
`npx three-blocks`.

It targets `WebGPURenderer`, including its WebGL fallback backend, not the
standalone legacy `WebGLRenderer`. The primary workflow registers
stable shader keys, captures their Three Shading Language (TSL) builds with
`npx three-blocks shaders capture`, and installs the resulting manifest through
`three-blocks/shaders` before scene compilation.

The stats panel is an optional development dependency:

```bash
npm i -D stats-gl
```

Declare each capture route in `three-blocks.shaders.json`; `three-blocks/vite`
inspects it directly. A strict build rejects a stale or missing manifest, while
normal development uses live TSL compilation. The `shaders`, `status`, `text`,
`browser`, and `environment` commands run the project's own copy of the command
engine. Install it once with `npm i -D @three-blocks/devtools` in any project the
scaffolder did not generate.

Register materials or compute nodes before compilation:

```ts
import { createShaderCache } from "three-blocks/shaders";

const shaders = createShaderCache("main");
shaders.material("main/subject", material);
```

Install the fresh manifest after `await renderer.init()` and before `compileAsync()`. `installShaderCache()` safely returns live mode when the receipt is missing, stale, invalid, targets the other backend, or needs an unsupported WebGL compute/PBO path. In the development server `threeBlocksShaders` carries `mode: 'live'`, so the same call compiles live until the overlay's **Preview production shaders** (`?tbShaders=precompiled`) asks for the manifest.

```ts
import {
  createThreeWebGLShaderCompatibility,
  createThreeWebGPUShaderCompatibility,
  installShaderCache,
} from "three-blocks/shaders";

const webgl = renderer.backend.isWebGLBackend === true;
const installation = await installShaderCache({
  renderer,
  scene: "main",
  state: threeBlocksShaders,
  loadManifest,
  compatibility: (webgl
    ? createThreeWebGLShaderCompatibility
    : createThreeWebGPUShaderCompatibility)({
    threeVersion: threeBlocksConfig.threeVersion,
  }),
  cache: shaders,
});
```

Register the page-owned `WebGPURenderer` once to expose the development overlay and optional stats panel. Production-aware bundlers select an import-free no-op entry. The internal `NODE_ENV` guard protects condition-less builds, and the Vite plugin also removes the development module:

```ts
import { registerDevtools } from "three-blocks/devtools";

registerDevtools({ renderer });
```

Capture and manifest installation remain separate from runtime diagnostics:

```bash
npx three-blocks shaders capture
npx three-blocks shaders status --check
npx three-blocks shaders test
```

The devtools runtime follows the core [PolyForm Noncommercial license](#license). Commercial use requires the commercial license. The runtime does not check entitlements. Custom-worker renderers should adopt `three-blocks/app`. See the [devtools guide](https://threejs-blocks.com/docs/tools/devtools) and [GPU ownership guide](https://threejs-blocks.com/docs/concepts/gpu-ownership).

### CLI

The package ships a zero-dependency CLI. Account, scaffolding, and setup commands
need no install:

```bash
npx three-blocks              # interactive menu
npx three-blocks starter app  # scaffold a new project
npx three-blocks login        # sign in (device flow)
npx three-blocks doctor       # check your setup
```

The engine-backed commands (`shaders`, `status`, `text`, `browser`,
`environment`, `optimize`, `assets`) also need `npm i -D @three-blocks/devtools`
unless the scaffolder generated the project. `shaders capture` also compresses
`public/` GLB and `.hdr` sources to meshopt + KTX2 automatically; `optimize`
runs the same engine on any file.

Credentials are stored in `~/.three-blocks/credentials.json` (0600) and are
never written to `.npmrc` or any other file. A separate random installation ID
is kept in `~/.three-blocks/config.json`; it is not derived from a credential
and signing out does not rotate it.

The CLI and managed Blender add-ons send small, first-party outcome events so
starter creation and committed tool outputs can be measured truthfully. They
contain only typed outcome/version fields, random installation/operation IDs,
and time — never the destination path, scene/project name, or access token.
Delivery is best-effort with a short timeout and cannot make a successful
command, bake, or export fail.

Set `TB_TELEMETRY=0` or `DO_NOT_TRACK=1` to disable CLI and Blender telemetry.
It is also disabled automatically in tests and CI, and for localhost or any
non-production `TB_SITE_URL`. Collector development can explicitly enable a
non-production site with `TB_TELEMETRY=1`; DNT, test, and CI guards still take
precedence. `three-blocks doctor` shows the effective state.

Installed Pro Tool versions run locally and offline, including after sign-out or
cancellation, for Projects covered by the commercial agreement. A credential and
active seat are required only to download or update a Pro Tool, use a hosted
service, or request account diagnostics.

## License

[PolyForm Noncommercial 1.0.0](./LICENSE). Personal and noncommercial use is
free. Pro grants each Project started during an active period a lifetime
commercial license for versions released during that period. After cancellation,
you may maintain, update, and distribute the Project with those covered versions
and keep using Pro Tool versions obtained while active for that Project locally
and offline. Starting a new commercial Project,
adopting a later version, or downloading or updating a Pro Tool requires an
active seat. Full terms are at the
[Commercial License Agreement](https://threejs-blocks.com/license),
plans at [threejs-blocks.com/pricing](https://threejs-blocks.com/pricing).
The starter, scaffolder, and Devtools use the same PolyForm Noncommercial
source boundary. They remain account-free and have no runtime gating.
