Skip to content

Troubleshooting ​

Symptom first, then the fix.

Setup ​

npm install fails with EBADENGINE ​

.npmrc sets engine-strict=true and the repository requires Node >= 24. Check node --version against .nvmrc. Do not work around it by disabling engine-strict; install the right Node.

ESLint reports no-bare-three-import ​

You wrote import { Mesh } from 'three'. Use three/webgpu (renderer and core), three/tsl (node materials) or three/addons/... (loaders and controls). The bare entry point pulls in the WebGL renderer and can create a second three singleton in the bundle, which breaks instanceof checks in confusing ways.

Rendering ​

Nothing renders and the console mentions requestAdapter ​

WebGPU is unavailable. Check chrome://gpu. Splat worlds fall back to the WebGL backend; splat characters do not — engine.caps.characters will be false and createCharacter rejects with CharacterUnsupportedError.

Splats look correct but transparent objects flicker through them ​

Splats draw after opaque geometry with depth test on and depth write off. A transparent mesh that intersects a splat volume has no correct ordering. Move the mesh out of the volume, or make it opaque.

renderer.backend.device is undefined ​

You read it before await renderer.init(). Bootstrap order is initWebGPUPatches() -> renderer.init() -> hand the device to onnxruntime-web -> create lift pipelines.

Wasm guest ​

The game behaves differently in npm run dev and npm run build ​

That is a parity bug between direct and wasm mode, and it is a real bug, not a configuration issue. Run the parity test; it hashes transform buffers from both modes over the same tape.

Every run produces identical "random" numbers ​

You seeded at module scope. Wizer snapshots the QuickJS heap at build time, so module-level state is frozen into the binary. Seed from env.seed() inside init().

TextDecoder is not defined (or a timer API is missing) ​

QuickJS does not ship the whole web platform. The SDK prelude polyfills what the engine needs; if you need something else, add it to the prelude rather than reaching for a browser global in guest code.

A command had no effect this frame ​

Commands are batched into frame-output and applied by the host afterwards. You cannot read back the result of a spawn in the same tick. The guest mints handles precisely so you do not need to.

Assets ​

Unknown asset id ​

The id is not in assets.json, or you used a path. Assets cross the boundary as string ids only. Add a manifest entry; docs/schemas/assets.schema.json is the shape it must have.

A binary file is a text stub after cloning ​

Git LFS is not installed and the file is LFS-tracked. Install LFS and git lfs pull. Note that fixtures/** and tests/fixtures/** are deliberately not LFS-tracked, so tests run on a clone without it.

See also ​