Skip to content

Troubleshooting & FAQ ​

Common problems, why they happen, and how to fix them - WebGPU support, GPU selection, performance tuning, exports that won't run, stale player templates, and saving.

Most issues fall into one of six buckets. Each section below states the symptom, the cause (what the engine is actually doing), and the fix. If your problem isn't here, open the Console - the editor surfaces GPU and script errors there, and a shipped game shows fatal errors over its boot splash.

WebGPU is not available ​

Symptom - the editor or a shipped game shows "WebGPU not available. Use Chromium 113+ or Safari 18+.", or a blank page.

Cause - Awaken3D renders entirely with WebGPU. There is no WebGL or Canvas2D fallback: the very first thing the engine does is check for navigator.gpu and request a GPU adapter, and if either step fails it throws WebGPUUnsupportedError rather than degrading. This check lives in initGPU() (packages/render/src/device.ts).

Fix - run in a browser that ships WebGPU enabled by default:

BrowserMinimumNotes
Chrome / Edge (Chromium)113+Best supported. Recommended for authoring.
Safari18+Works for playing; some optional GPU features may be absent.
FirefoxRecentImproving, but adapter names are withheld (see the GPU picker below).

Then check, in order:

  1. Update the browser. WebGPU shipped stable in Chromium 113 (2023). Older builds either lack it or hide it behind a flag.
  2. Verify support directly. Open the JavaScript console and run !!navigator.gpu. false means the browser (or your environment) is not exposing WebGPU at all.
  3. Enterprise / managed devices. Corporate policy, a locked-down GPU allow-list, or a disabled hardware-acceleration setting can strip WebGPU even on a current Chrome. Check chrome://gpu - "WebGPU: Hardware accelerated" confirms it is live. If it is disabled by policy, WebGPU cannot be re-enabled from the page; it must be changed in the browser/enterprise settings.
  4. Remote desktops / VMs / headless CI frequently have no usable GPU adapter. navigator.gpu may exist but requestAdapter() returns null, producing "No suitable GPU adapter found."

πŸ“Έ Screenshot - save as img/troubleshooting-webgpu-unsupported.png

The editor's WebGPU-unsupported message shown on a browser without WebGPU, with the browser's version string visible.

TIP

Runtime GPU errors that happen after startup are not fatal. The engine listens for uncapturederror and prints them to the console as [awaken gpu] … (and into the on-page #gpu-error slot) instead of failing to a black canvas - so a shader/validation error shows a message, not a blank screen.

Choosing a GPU: integrated vs discrete ​

Symptom - the editor or game feels sluggish on a laptop that has a discrete graphics card, and the stats overlay names an integrated GPU (e.g. an Intel/AMD iGPU) rather than the discrete one.

Cause - WebGPU gives the page no way to enumerate GPUs. The only control is a powerPreference hint ("high-performance" or "low-power"). On a hybrid-graphics laptop the browser usually maps "high-performance" to the discrete GPU and "low-power" to the integrated one - but it can still hand back the integrated GPU even when high-performance is requested, depending on OS and driver policy.

Awaken3D works around this by probing both power preferences at startup (probeAdapters() in packages/render/src/device.ts). If the two probes come back as genuinely different adapters - distinguished by name, or, when the browser withholds names (Firefox, Chrome's fingerprint-resistance), by a capability fingerprint of their limits and features - it offers a GPU picker:

  • In the editor, a small GPU dropdown appears in the scene Viewport, next to the stats (β“˜) button - only on a hybrid-graphics machine with more than one GPU. (The GPU cull checkbox is a different control that lives in Render Settings.)
  • In a shipped game, a small corner dropdown labelled GPU appears (only when there is a real choice - single-GPU machines never see it).

Fix

  • Pick the discrete GPU from the picker for best performance. Selecting an option saves your choice to localStorage (key awaken.gpu.powerPreference) and reloads the page, because switching GPUs requires a fresh WebGPU device. The default is "high-performance" (prefer discrete).
  • Confirm which GPU is actually in use via the stats overlay - it prints the adapter label and the current preference (… Β· pref: high-performance / low-power). Open it with the β“˜ button in the editor's Viewport; in a shipped game press F3 or ` to toggle it.
  • No picker appears but you're still on the weak GPU? Then the browser is exposing only one adapter to the page - JavaScript cannot override that. Force the discrete GPU in your OS / driver graphics settings (Windows Graphics settings, NVIDIA/AMD control panel, or macOS) for the browser, then relaunch.

πŸ“Έ Screenshot - save as img/troubleshooting-gpu-picker.png

The GPU picker showing a discrete and an integrated option, with the stats overlay's "GPU device …" line visible confirming the active adapter.

The editor or game runs slowly ​

Symptom - low frame rate, especially in large imported scenes.

Cause first, then knobs. Before changing settings, understand where big-scene cost comes from. Historically the culprit was not WebGPU present cadence - it was a per-frame full scene-cache rebuild. The renderer keeps a GPU-resident cache and only rebuilds it fully on a structural change (spawn, destroy, enable/disable, reparent - tracked by World.structureRev). Merely moving objects is cheap: the engine recomputes only the moved subtrees and patches only their GPU rows, so a 15,000-object static scene with one spinning prop costs one row of work per frame, not 15,000. If your scene is slow while nothing is being spawned or destroyed, the fix is in the render settings below, not in the frame loop.

All of these live in Render Settings and ship inside the exported game:

SettingWhat it doesWhen to turn it on
Static batchingMerges unique static meshes per grid cell into single draw calls. Moving objects stay separate.Any scenery-heavy scene - cuts draw calls. Watch the Draw calls stat.
Cached shadowsRe-renders only moving shadow casters each frame; static casters are baked once and re-baked only when the sun angle or geometry changes.Mostly-static scenes with shadows. Big win.
Draw distanceSize-aware distance culling: an object stops drawing past (its bounding radius Γ— this value). Big things (buildings, terrain) stay visible far; small props cull close. 0 = off.Large open worlds / weaker devices. Try ~300–500; lower is more aggressive.
Render scaleInternal render resolution (0.25–1.0); the browser upscales to the viewport. Below 100% cuts GPU fill/bandwidth a lot (softer image).Fill-bound scenes. If lowering this jumps FPS, you were fill-bound.
Anti-aliasing (MSAA)4Γ— or off. Off only helps scenes limited by fill/bandwidth.Turn off to reclaim fill on a fill-bound scene; leave on otherwise.
Shadow distanceMaximum range (20–400 m) that casts shadows. Beyond it, no shadows.Lower it if shadows are expensive and distant shadows don't matter.
Merge budgetHow much static geometry to merge into big chunks (in millions of verts) before falling back to per-object draws.Raise on heavy scenes to cut draw calls further (costs merged-geometry GPU memory).
GPU cull (experimental)Frustum-culls every object on the GPU and draws the main pass with indirect draws, so the CPU does no per-object culling.Only if your GPU supports it - see below.

A tuning order that works: enable Static batching and Cached shadows first (they help almost every scene), then add a Draw distance for open worlds, and only drop Render scale / MSAA if the stats show you are fill-bound (lowering render scale jumps the FPS).

About "GPU cull" - this is opt-in and requires the indirect-first-instance WebGPU feature. The engine records whether the device has it in GPUCaps.indirectFirstInstance (packages/render/src/device.ts). If the checkbox is disabled/greyed with an "Unavailable" tooltip, your current GPU lacks the feature and the option would silently fall back to CPU culling. On a hybrid laptop, try switching to the discrete GPU (see above) - it may expose the feature the integrated one doesn't.

πŸ“Έ Screenshot - save as img/troubleshooting-perf-settings.png

Render Settings with Static batching and Cached shadows enabled, a Draw distance set, and the stats overlay showing FPS + Draw calls.

See Render Performance and Static Batching for the systems behind these knobs.

My exported game is black or won't run ​

This is almost always a file access problem, not a rendering one. Awaken3D exports in three shapes, and they have different launch requirements. Pick the right one for how you intend to run it.

Single-file game.html (double-clickable) ​

The default export (File β–Έ Export Game) is one self-contained game.html. It is engineered to run straight from a file:// origin: the scene is embedded as a base64 blob in window.__AWAKEN_SCENE_BIN__, behaviours are injected as classic inline scripts (ES modules don't execute from file://), and it decompresses with fzstd (pure JavaScript, no network). So double-clicking it should work.

If it is black:

  • Check the console. "No game scene found" over the splash means the scene payload didn't embed - re-export. GPU errors print as [awaken gpu] ….
  • Physics on file://. If the scene uses colliders/rigidbodies, the player tries to load Rapier's WASM. On some file:// setups that fetch fails - this is caught, so the game still renders (physics just stays off) and logs a warning; it does not white-screen.

Folder export: game.html + scene.fg (must be served) ​

File β–Έ Export Game Folder writes a game.html plus a separate scene.fg (the raw compressed scene - smallest total size, no base64 bloat). This game.html fetches ./scene.fg at load. Browsers block fetch from file://, so:

  • Double-clicking it will not work - the fetch is blocked and the game finds no scene.
  • You must serve the folder: upload it to a host (itch.io, any static host), or run a local server (npx serve, python -m http.server, etc.) and open it over http://.
  • Folder export needs a Chromium browser (it uses the directory picker).

If you want a locally double-clickable file, use the single-file game.html export instead.

Preview ​

Preview opens the game in a new tab served over http:// by the dev-server middleware - a clean, file://-free run that matches hosting. It needs the built player template; if the middleware isn't present (a plain built editor), it falls back to a blob: URL. If Preview reports "Preview needs the player template - run pnpm build:player-template", build the template (next section).

ExportCommandRuns from file://?Needs serving?
Single-file game.htmlFile β–Έ Export Gameβœ… Yes (double-click)No
Folder (game.html + scene.fg)File β–Έ Export Game Folder❌ No (fetch blocked)βœ… Yes (host / local server)
scene.awaken.jsonExport Game without a player template❌ NoDrop beside a hosted player

See Export as Single File and Export as Folder.

A feature works in the editor but is missing from the export (stale player template) ​

Symptom - a behaviour, component, or render feature works in-editor and in Preview, but is absent or broken in an exported game.html.

Cause - the exported game.html embeds a pre-built player template. The export code fetches /player-template.html and injects your scene into it. If that template was built against older engine/player code, the export runs the stale player even though the live editor runs the new code. (If the template isn't served at all, export silently falls back to scene.awaken.json instead of game.html.)

Fix - rebuild the template after any engine or player change, then re-export:

bash
pnpm build:player-template

WARNING

This is easy to miss because everything looks correct in the editor. Whenever you pull engine changes or edit apps/player, run pnpm build:player-template before exporting, or the shipped game runs code that predates your change.

See Ship Overview and the Player page.

I lost changes / the "unsaved changes" warning ​

Symptom - a reload or tab-close lost recent edits, or the browser warned you about leaving with unsaved work.

Cause - auto-save is off. Awaken3D does not continuously write your project (it once did; it was removed to avoid surprise disk churn and to never bake in-play state). Your edits live in memory until you explicitly save. A beforeunload guard warns you if you try to leave while the project is dirty (has unsaved changes) - that browser prompt is the safety net, not a bug.

Fix

  • Save with ⌘S / Ctrl+S (or File β–Έ Save). On Chromium this writes in place to your .awaken file via the File System Access API; on Firefox/Safari it downloads the project instead.
  • Save is blocked during Play. If you press Save while playing, you'll see "Stop play before saving" - saving mid-play would bake transient play-time state (physics positions, accumulated rotations) into the scene. Stop first, then save.
  • Heed the leave warning. If the browser asks "Leave site? Changes you made may not be saved", you have unsaved edits - cancel and save, then leave.

πŸ“Έ Screenshot - save as img/troubleshooting-unsaved-warning.png

The browser's beforeunload "unsaved changes" prompt appearing when closing the editor tab with a dirty project, with the editor's unsaved indicator visible.

See Projects & Saving and Keyboard Shortcuts.

See also ​

Awaken β€” browser-native WebGPU game engine.