Troubleshooting and deployment

Find the fix for common engine problems, then check a build before shipping.

Setup and rendering

Most blank screens are a missing native build, an unawaited warm-up or a view with no size. Log onError first.

Setup and rendering reference
SymptomFix
Blank viewGive it a size; check onError, camera direction, entity scale and paused.
isEngineAvailable is false / core not linkedExpo Go or an old build: make a new development build.
"await warmUpEngine" errorAwait it before new World or procedural meshes; useWorld does it.
WASM fails with a MIME or HTML errorServe the real .wasm file as application/wasm from the right path.
No WebGPU or audio workletUse HTTPS or localhost; WebGL2 is the fallback.
Paused after navigationPass the real focus state to paused and clear held input.

Guard and log

React and React Native · shared engine API

Guard and log
import { GameView, isEngineAvailable, type World } from "@nayan-ui/engine";
export function SafeView({ world }: { world: World }) {
if (!isEngineAvailable) return null; // show "needs a development build"
return <GameView source={world} onError={(error) => console.error("Engine:", error.message)} style={{ flex: 1 }} />;
}

Worlds, frames and queries

After dispose, cleanup calls and queries are no-ops, but spawn, update, burst and effects throw. Check world.isDisposed after every await before spawning.

Worlds, frames and queries reference
SymptomFix
"World: used after dispose()"Check world.isDisposed after awaits; never reuse a disposed world.
world.transforms moves don't show yetThey apply on the step; flush() first to read them back.
createTransformBatch moves never applyExtra batches aren't flushed for you: call flush().
Game runs at double speedRemove extra world.update calls and rAF or setInterval loops.
toScreen / screenToWorld return nullNeeds a drawn frame; null also means behind camera or edge-on.
Blank Android screenshotUse captureScreen(..., { handleGLSurfaceViewOnAndroid: true }).

Spawn safely after an await

React and React Native · shared engine API

Spawn safely after an await
import { loadModel, type World } from "@nayan-ui/engine";
export async function addBoss(world: World, url: string) {
const boss = await loadModel(url, { fit: 3 });
if (world.isDisposed) return null; // the screen closed while loading
return world.spawn({ mesh: boss, position: [0, 0, -8] });
}

Assets, animation and feedback

When a clip or asset fails, print what the model actually contains before changing code.

Assets, animation and feedback reference
SymptomFix
Bundled asset fails in an Android releaseInstall expo-asset, add glb/gltf to assetExts, test a release build.
Web asset is a numberUse URL strings on the web instead of require(...).
Clip doesn't playCheck model.animations names; export clips into a self-contained GLB.
Model at the wrong originReview center, fit and the model's own coordinates.
pick returns nullWait for a drawn frame, use view coordinates, keep pickable true.
No sound on the webOutput starts on the first click, touch or key; await bank.ready.
No vibrationCheck haptics.enabled and haptics.supported; simulators have none.
Out of entitiesRaise capacity; text letters and particles take slots.
Out of mesh idsA mesh is built per spawn or frame: build once (getMeshIdLimit).
Loader freezes on first launchSlice the job with afterNextFrame and cache it with ProceduralCache.

Name the clips when one fails

React and React Native · shared engine API

Name the clips when one fails
import type { Entity, LoadedModel, World } from "@nayan-ui/engine";
export function play(world: World, actor: Entity, model: LoadedModel, clip: string) {
if (!world.playClip(actor, clip)) {
console.warn(`No clip "${clip}". Available: ${model.animations.map((a) => a.name).join(", ")}`);
}
}

Production checklist

Test production builds on real devices: a rebuilt engine library doesn't prove the installed app binary was rebuilt.

Production checklist reference
CheckWhat to verify
WebProduction build over HTTP: asset URLs, .wasm MIME, WebGPU and WebGL2.
Input and layoutResizing, tap, pinch and overlay touches.
LifecyclePause, resume, hidden tabs and backgrounding the app.
FeedbackFirst-interaction audio, sound and haptics settings.
NativeFresh iOS and Android builds after engine or config changes.
RunsEach run reports once, including cancel, and cleans up.

Serve a production web build

Terminal / configuration

Vite shown; serve any framework's built output the same way, then test on real devices.

Serve a production web build
bun run build # vite build
bunx vite preview # serves dist/ as production will