Status: 5.0.0 released 2026-11-03 and superseded by 5.0.1 (2026-11-05); install 5.0.1. This guide covers every breaking and behavior change between Aura3D 4.7.16 and 5.0.0, checked against the packed 5.0.0 packages.
5.0.1 note. 5.0.1 is 5.0.0 plus render fixes; the public API and every row below are unchanged. 5.0.0 blanked the showcase games because contact shadows and eleven other new hard throws rejected the mount. In 5.0.1 those paths degrade with a one-time console.warn code (CONTACT_SHADOW_DEGRADED, DEPTH_ALPHA_HASH_DEGRADED, DEPTH_ALPHA_COVERAGE_DEGRADED, TRANSMISSION_DEGRADED, DIFFUSE_TRANSMISSION_DEGRADED, TEXTURE_FLIPY_DEGRADED); strict mode still throws. Contact shadows are skipped in scenes with an extra contributor pass or a transparent queue. Behaviours quoted below as 5.0.0 behaviour that throw on these paths apply to strict mode only in 5.0.1. See the changelog.
1. Summary
5.0.0 is 4.7.16 plus the native-capability program (roadmap, decision record). Most of the change is additive: new subpaths, a new @aura3d/addons package, an explicit readiness contract, request-policy-aware asset loading, and opt-in WebGPU, worker, depth, color and volumetric paths. Under the deprecation policy, 5.0.0 also removes what 4.x scheduled for removal: nine legacy @aura3d/engine subpaths and 38 deprecated root values (B14, B15). Nothing first deprecated in 5.x is removed.
The breaking changes are narrower than the major number suggests:
- the root
createGamenow throws a migration error instead of returning a headless stub; - a few public types gained required members or wider unions, which breaks code that implements those types or switches over them exhaustively;
renderer.backendis typed and validated, and theexperimental-webgpuquality profile now requests WebGPU instead of throwing;- strict-mount failures reject with a different error object;
- the experimental
@aura3d/reactadapter changes whenonReadyfires and how failures and shared resources behave; - the deprecated 4.x root values and legacy subpaths are removed (B14, B15);
@aura3d/assetsresolves its own browser declarations under thebrowsercondition (B16);ConvexHulland its face, edge and vertex classes are the independent hull: structural inputs, frozenHullVector3results, bounded construction (B17).
Aura3D remains a standalone engine with its own renderer. WebGL2 remains the default backend.
2. Who is affected
| You... | Impact |
|---|---|
Write scenes with createAuraApp, scene, looks, model(assets.x) and no type-level mocks | Usually none. Upgrade, rebuild, and run check-deploy. |
Call createGame from @aura3d/engine (the root) | Breaking. It now throws. See B1. |
Implement AuraApp, AuraRuntimeNodeHandle or AuraRuntimeNodeSnapshot yourself (test doubles, adapters) | Breaking at type level. See B2. |
switch exhaustively over diagnostics or asset/material unions | Breaking at type level. See B3. |
Pass renderer.backend or qualityProfile: "experimental-webgpu" | Behavior change. See B4 and B5. |
Use strict: true and inspect the app.ready() rejection | Behavior change. See B6. |
Use @aura3d/react (experimental) | Behavior change. See B7 to B9. |
| Load credentialed, cross-origin or very large assets | Behavior change. See section 5. |
Import a deprecated 4.x root value or a legacy @aura3d/engine/* subpath | Breaking. Removed in 5.0.0. See B14 and B15. |
3. Upgrade steps
Upgrade every
@aura3d/*package together. Mixed 4.x/5.x installs are not a supported combination.npm install @aura3d/engine@^5.0.0 @aura3d/cli@^5.0.0 # plus any of: @aura3d/react @aura3d/assets @aura3d/rendering @aura3d/addons ...- For a new project, scaffold with
npx create-aura3d@latest my-scene --template <template>. It writes the currentllms.txtand Aura3D skills. For an existing project, refresh the skills with the CLI version you install. - Peer dependencies.
@aura3d/enginekeeps its optional peers unchanged:@dimforge/rapier3d-compat0.20.0andrecast-navigation0.43.1.@aura3d/reactdeclaresreact^19.0.0 || ^18.0.0(unchanged) and@aura3d/engine^5.0.0(was^4.7.16). - Fix the compile errors from section 4. Then search your code for
createGameimported from"@aura3d/engine", for the removed root values (B14) and for the removed subpaths (B15). For the renderer subpaths,npx @aura3d/cli@latest codemod renderer-imports "src/**/*.ts" --writerewrites@aura3d/engine/advanced-runtimeand/production-runtimeimports to@aura3d/engine/renderer. - Rebuild, then run
npx @aura3d/cli@latest check-deploy --dist dist. In this monorepo, run browser checks on the remote runner (tools/remote-acr/run.sh).
4. Breaking changes
| # | Before (4.x) | After (5.0) | Action | Evidence |
|---|---|---|---|---|
| B1 | Root createGame from @aura3d/engine returned a headless stub (no HUD, touch, juice or sound) and logged CREATE_GAME_STUB. | It throws AuraGameMigrationError before mounting: message prefix CREATE_GAME_ROOT_UNAVAILABLE_V1, code: "CREATE_GAME_ROOT_UNAVAILABLE", version: 1. The staged @aura3d/engine/v5 root throws the same error. | Import createGame from @aura3d/engine/game (or @aura3d/engine/v5/game with the staged entry). If the game subpath reports the same code, remove any -game / none quality-rebuild opt-out and enable A3D_QR_GAME. | packages/engine/src/contracts/stubs/game.ts, packages/engine/src/public/v5-internal.ts, docs/agents/claims-and-boundaries.md |
| B2 | AuraApp, AuraRuntimeNodeHandle and AuraRuntimeNodeSnapshot had no pivot, text or readiness members. | Required members were added. AuraApp: readiness() and readyStrict(). AuraRuntimeNodeHandle: pivot, setPivot(), setTransform() and setText(). AuraRuntimeNodeSnapshot: pivot. | Code that only calls the engine needs no change. Hand-written implementations and mocks must add these members. | packages/engine/src/agent-api/nodes/types/runtime.ts (AuraRuntimeNodeSnapshot.pivot, AuraRuntimeNodeHandle lines 163-175, AuraApp lines 416-418) |
| B3 | Narrower unions: renderer diagnostics outputColorSpace: "srgb"; AuraAssetType had 6 members; AuraModelFormat = "glb" | "gltf"; material kind?: "custom"; material texture?: AuraAssetRef<"texture">. | Wider unions: outputColorSpace: "srgb" | "display-p3" | "unknown"; AuraAssetType adds "gaussian-splat", "photometric", "scalar-volume"; AuraModelFormat also accepts any registered adapter string; kind adds "depth-preview"; texture also accepts a live AuraTextureRef. | Add default / unknown branches to exhaustive switches. Do not assume the output color space is sRGB: read the diagnostic. | packages/engine/src/agent-api/nodes/types.ts (lines 50, 53, 245, 329, 1385) |
| B4 | renderer.backend was typed unknown, and any value passed type-checking. | It is typed "webgl2" | "webgpu" | "auto". Any other value throws BACKEND_OPTION_INVALID:renderer.backend=.... allowFallback must be a boolean, and requiredFeatures needs webgpu or auto. | Remove invalid values, or use one of the three. The default stays webgl2. | packages/engine/src/agent-api/nodes/types.ts:1341, packages/engine/src/agent-api/app/rendererOptions.ts (resolveBackendOptions) |
| B5 | renderer.qualityProfile: "experimental-webgpu" threw AuraMigrationError ("frozen by PRD 11"). | It resolves and requests the WebGPU backend. Because allowFallback defaults to false for webgpu, it fails on a device without WebGPU unless you opt into fallback. | Remove the profile, or pass renderer: { backend: "auto" } (fallback on by default) or { backend: "webgpu", allowFallback: true }. | origin/main:packages/engine/src/agent-api/app/rendererOptions.ts (resolveRendererQualityProfile) vs current rendererOptions.ts |
| B6 | With strict: true, a failed mount rejected app.ready() with the original AuraRuntimeError("renderer-mount-failed"), including its cause. | It rejects with a new Error that carries the bounded, redacted message and a string code (still "renderer-mount-failed" for an ordinary mount failure). instanceof AuraRuntimeError and .cause no longer apply. | Branch on error.code, or use await app.readiness() / app.readyStrict() (section 5.1). | origin/main:packages/engine/src/agent-api/app/createAuraApp.ts (T4.2 block) vs packages/engine/src/agent-api/app/createAuraAppCore.ts (T4.2 block, readinessErrorCode) |
| B7 | @aura3d/react AuraCanvas called onReady when the app was created. Failures were reported only through diagnostics. | onReady fires once per created app, and only after a successful renderer readiness outcome. useAuraApp() is undefined until then and while a changed scene remounts. Without onError, a creation, readiness or scene-update failure is thrown during render to the nearest error boundary as AuraCanvasReadinessError (state, code). | Pass onError, or wrap the canvas in an error boundary. To retry, remount with a new React key. Do not treat onReady as "first frame drawn". | packages/react/README.md (Canvas lifetime and readiness), packages/react/src/index.ts (AuraCanvasReadinessError) |
| B8 | resourceForDescriptors(descriptors, loader) shared a resource for any custom loader. | A custom loader without a securityContext object throws (Aura3D custom loaders require a securityContext for shared resources). Model with a custom loader and no context gets its own instance scope. | Pass a stable securityContext object when sharing is intended, or use createAuraAssetResource() for an independent resource. | packages/react/src/index.ts:1143-1154, packages/react/README.md (Preload resources and ownership) |
| B9 | Scene rendered null. useAuraAsset(resource) only called read(). AuraAssetResource.preload() took no arguments. | Scene renders its children. useAuraAsset retains the resource until effect cleanup. preload({ signal }) is cancellable per caller. During SSR and the first hydration render, Model renders its fallback without starting the preload. Incomplete evidence throws AuraAssetPreloadError. | Check layouts that relied on Scene rendering nothing. Implementations of AuraAssetResource must accept the optional options argument and provide retain(). | docs/api/public-api.md (@aura3d/react declarations), packages/react/README.md |
| B10 | game.shell.setLoadingProgress() and game.shell.track() on the full game entry (@aura3d/engine/game) accepted calls and did nothing. | They drive a game-owned loading screen with a <progress> meter: progress is the larger of the manual fraction (clamped to 0–1) and settled ÷ tracked weight (a rejected promise counts as settled; the caller keeps the rejection). The screen shows only in the loading state and is removed on dispose. A NaN fraction throws GAME_LOADING_FRACTION_INVALID_V1; a negative or non-finite weight throws GAME_LOADING_WEIGHT_INVALID_V1; calls without a document body throw GAME_SHELL_LOADING_UNAVAILABLE_V1; calls after dispose throw GAME_DISPOSED_V1. | Remove any custom loading overlay that duplicated these calls, and call them only from a browser host (use createGameShell with a document and root for a custom or headless host). | packages/game/src/components.ts, tests/unit/game/shell-loading-progress.test.ts |
| B11 | @aura3d/rendering/contracts exported setTemporalHistoryImpl(history), which registered one module-global temporal history that every unscoped reset reached. | Removed. There is no module-global temporal history: each renderer, device or view owns its own (createScopedVelocityHistory(flags) or the renderer's per-view TemporalHistory). | Delete the registration. Keep the history you created and reset it directly, or pass it to resetTemporalHistory(reason, history) (B13). | packages/rendering/src/contracts/velocity.ts (git show ed5a52d58^: vs ed5a52d58), tests/unit/rendering/native-velocity-compat-scope.test.ts |
| B12 | VelocitySurface (the C-14 velocityHistorySlot provider, exported from @aura3d/rendering and @aura3d/rendering/lanes) had a required history member: a shared compatibility history used by every caller. | VelocitySurface.history is removed. The surface has only kind and createHistory(), which must return a fresh history each call. | Read histories from createScopedVelocityHistory(flags) instead of surface.history. Custom providers drop history and implement createHistory(). | packages/rendering/src/forward/Velocity.ts (VelocitySurface), tests/unit/rendering/prd03-velocity.test.ts |
| B13 | resetTemporalHistory(reason, target?) accepted an omitted target and then reset the registered global history (or did nothing). | resetTemporalHistory(reason, target) requires a target: a renderer- or device-owned history or a view adapter with reset(reason). A missing or invalid target throws TypeError ("resetTemporalHistory requires an explicit renderer, device or view history target"). | Pass the history (or view adapter) you want to reset. App code should keep using app.camera.cut(), which already targets the active view. | packages/rendering/src/contracts/velocity.ts (resetTemporalHistory), tests/unit/rendering/C-14-velocity.test.ts |
| B14 | 42 root values were deprecated in 4.x with "Removal from the root planned for 5.0.0". | The 38 still exported in 4.7.16 are removed from @aura3d/engine (table below). The four 4.0.0 compatibility stubs (createAssetDiagnostics, createDiagnosticsPanel, createEnvironment, workflows) are unchanged. The nine deprecated type-only root exports (AuraCreateAppOptions, AuraCreateGameAppOptions, AuraDiagnosticsOverlay, AuraEffectNode, AuraLightNode, AuraModelOptions, AuraPerformanceQuality, AuraPrimitiveNode, CharacterAssemblyValidationReport) stay in 5.0.0 because public signatures and @aura3d/react still reference them. | Import each name from its replacement below. Names marked "no replacement" were internal helpers; remove the call. | packages/engine/src/public/index.ts, root export list, tests/unit/physics/native-v5-entry.test.ts |
| B15 | Ten legacy @aura3d/engine subpaths were deprecated re-export stubs (console warning, removeIn: "5.0.0" in aura.exports.json). | Nine are removed from the exports map and the stubs are deleted (table below). Importing one fails to resolve. @aura3d/engine/media-node is no longer deprecated: it is the Node-only media entry (FFmpeg, PNG sequences, audio mux, captions, publishing) and its scheduled target, the browser root, cannot carry Node built-ins. The renderer subpaths' documented destination, @aura3d/engine/renderer, now re-exports the engine production-runtime SDK (loadGltfScene, loadHdrEnvironment, createProductViewer, createStudioLighting, createTypedGLBActor, the game render presets and createPerformanceGovernor, among others) plus Texture, Sampler, AnimationToonMaterial, quantizeToonBand and the CollectedLight type. | Change the import specifier as below. | aura.exports.json, package.json#exports, packages/engine/src/public/renderer.ts, tests/unit/tools/packed-consumer-check.test.ts |
| B16 | The @aura3d/assets root entry and the @aura3d/engine/assets subpath listed one types target (dist/index.d.ts) for every condition, so the browser condition (dist/browser-index.js) was typed with Node-only helpers that the browser runtime does not export (for example createAssetImportPreflightReport, detectAssetImportFormat, loadProductionAssetManifest). The browser condition was a bare string target. | The browser condition is an object with its own types (dist/browser-index.d.ts, or dist/assets/browser-index.d.ts for @aura3d/engine/assets) and default (the matching browser-index.js), listed before the shared types and import conditions. Bundlers and moduleResolution: "bundler" projects that set the browser condition now see the browser surface; Node and NodeNext resolution are unchanged. Code built for the browser that imports a Node-only helper from @aura3d/assets now fails to type-check instead of failing at run time. | In browser builds import only what @aura3d/assets/browser (or @aura3d/engine/assets) exports under the browser condition; the Node-only helpers stay available through the default (Node) entry. They were never present at run time under the browser condition. | packages/assets/package.json#exports, package.json#exports["./assets"] (generated from aura.exports.json browserTypes), packages/assets/src/browser-index.ts, tools/public-surface-diff/index.ts |
| B17 | ConvexHull, ConvexHullFace, HalfEdge, VertexNode and VertexList from @aura3d/math were typed on Vector3 and Ray: vertex.point, face.normal, face.midpoint and intersectRay() were Vector3 instances, construction accepted any finite point count, and the hull kept its partial state after invalid input. | The classes are Aura3D's independently written hull (packages/math/src/independent/ConvexHull.ts). Inputs are structural ({ x, y, z } points, { origin, direction } rays, so Vector3 and Ray instances still work), but results are frozen HullVector3 values (x, y, z, length()), not Vector3. Construction is bounded (100,000 input points, 8,192 distinct points, 25 million face tests) and throws RangeError beyond that or on non-finite input; intersectRay() throws RangeError for a ray origin farther than 2^48 hull scales from the center; face and edge snapshots are rebuilt by compute() or setFromPoints(); list nodes enforce ownership (RangeError when moved between lists). ShapeUtils, earcut, shapeArea, isClockWise and triangulateShape keep their names and behavior but take structural { x, y } points instead of Vector2 (a Vector2 is accepted). | If you read hull results as Vector3, convert with new Vector3(p.x, p.y, p.z). Split hulls larger than the bounds. Do not rely on a hull surviving a thrown setFromPoints(). | packages/math/src/ConvexHull.ts, packages/math/src/independent/ConvexHull.ts, tests/unit/math/native-convex-hull.test.ts, tests/unit/math/native-independent-hull-entry.test.ts, tests/unit/math/native-root-convex-hull.test.ts |
@aura3d/react is experimental (support policy), so B7 to B9 fall outside the stable-API window. They are listed here because they change observable behavior.
5. Behavior changes
These need no code change for most apps, but they change what an existing call does.
5.1 Readiness: typed success, failure and cancellation
app.ready() keeps its 4.x contract. It settles when the mount settles, and in non-strict mode it resolves even if the mount failed. Two calls are new:
const outcome = await app.readiness({ stage: "first-frame", signal });
// { state: "ready", stage, backend } | { state: "failed", code, message }
// | { state: "cancelled" | "disposed" } | { state: "superseded" }
await app.readyStrict(); // rejects on failure (AuraReadinessError), cancellation (AbortError), disposal or supersession
Each non-ready outcome has one cause, and the app's own mount state never changes because a waiter stopped waiting:
| Outcome | Cause | readyStrict() rejects with |
|---|---|---|
failed | The mount for the current scene failed (code, bounded and redacted message); also device-lost while the device is lost. | AuraReadinessError, code = the failure code |
cancelled | Only the caller's own signal aborted (before or while waiting). The mount keeps running and later waiters still see its result. | AbortError, code: "cancelled" |
superseded | app.setScene() replaced the mount this waiter was waiting for. Wait again to follow the new mount. | AuraReadinessError, code: "superseded" |
disposed | app.dispose() ran before the stage was reached, or readiness() was called on a disposed app. | AuraReadinessError, code: "disposed" |
app.onRenderTermination(listener) and app.renderTermination() report a mount's terminal state (failed, device-lost, superseded, disposed) with an app-local revision. Subscribe first, then read renderTermination(), because events are not replayed. Source: packages/engine/src/agent-api/nodes/types/runtime.ts (AuraReadyOutcome, AuraRenderTermination, AuraReadinessOptions), stability policy.
5.2 Renderer mount failures and canvas ownership
- A WebGPU or
autoapp cannot share a canvas or device that another managed renderer already owns. The codesWEBGPU_CANVAS_IN_USE,WEBGPU_DEVICE_IN_USEandWEBGPU_OWNERSHIP_CLEANUP_FAILEDrejectapp.ready()**even withoutstrict**, and they do not paint an error overlay on the peer's canvas (createAuraAppCore.ts,managedCanvasInUse, ownership branch of the mount handler). - When no WebGL2 or WebGPU renderer is available, the mount records
renderer-unavailableinreadiness().
5.3 Disposal and cancellation
Remount cleanup now runs every owned disposer (unsubscribe, compute, controller, physics) separately and aggregates their failures, so one failing disposer no longer skips the others. Renderer resource retirement after disposal can be awaited through the renderer controller's whenDisposed(). Cancelled asset decodes are not dispatched, and a decoder result that arrives after cancellation is closed.
A resource whose cleanup fails stays owned and reported rather than being dropped: the failure rejects app.disposeAsync() (or throws from dispose() on the owning handle), and the next call retries only the failed resource, never ones that already retired. This includes a ProgramCache shader program whose disposer threw, also when the program arrived late, after the cache was disposed (ProgramCache.disposalFailures(device) marks entries resolved once a retry succeeds).
Disposing an app cancels every load it owns: texture.load(url, { app }), texture.loadNativeAsset(asset, { app }) and scene model loads reject at once with an AbortError. Their network requests receive an aborted signal even when you passed none. A load that fails after its app closed is not reported to that app's onAssetError, and new loads for a closed app are rejected before any request starts. Call app.dispose() when an app is no longer needed and release resources through their owning handle (stability policy; audit, WP-07 and WP-21 entries).
Typed GLB actors release registered-format (non-glTF adapter) resources asynchronously after actor.dispose(). awaitTypedGLBActorDisposals (new in 5.0.0, exported from @aura3d/engine/renderer next to createTypedGLBActor) waits for that teardown and rejects with an AggregateError ("Native model cleanup failed") if it failed. Call it with no argument to drain actors you created with createTypedGLBActor directly (no runtimeOwner); it never waits on app-owned actors, and each ownerless failure is reported once. awaitTypedGLBActorDisposals(owner) drains only the actors created with that runtimeOwner; app.disposeAsync() already does this for the actors an app mounts.
import { awaitTypedGLBActorDisposals, createTypedGLBActor } from "@aura3d/engine/renderer";
const actor = await createTypedGLBActor({ asset: assets.robot, id: "robot", width, height, formatRegistry });
actor.dispose();
await awaitTypedGLBActorDisposals(); // ownerless actors
5.4 Asset loading: request policy, credentials and cache partitioning
AssetRequestOptions(@aura3d/assets) addscredentials,headers,cache,priority,mode,redirect,referrerPolicy,allowedOrigins,sourceOrigin,expectedUrl,maxBytes,integrityandsecurityContext. One policy covers a document and its dependencies, and credentials do not leak to other origins (packages/assets/src/AssetLoader.ts).- **Without a
securityContext, requests that may carry browser credentials are neither shared nor cached.** To keep cache reuse for credentialed loads, set a non-secret generation per app (createAuraApp(canvas, { assets: { securityContext: "session-42" } })). Dispose and recreate the app when credentials change. An empty or non-string value throws (packages/engine/src/contracts/assets.ts,snapshotAuraAssetsOption). - Default limits: browser raster images in glTF are capped at 64 MiB input and 32 Mi-pixels (
packages/assets/src/BrowserImageDimensions.ts). KTX2 input is capped at 64 MiB while it is fetched, not only after it is fully loaded. Larger images that loaded in 4.x are now rejected with a diagnostic. - Error messages and diagnostics redact URLs and cause details (
AssetLoadError, glTF fallback diagnostics). - glTF now accepts finite, non-negative
alphaCutoffvalues above 1 (a relaxation).
5.5 Decoder registration and native formats
- glTF/GLB is built in. Other model formats need an adapter registered per app through
createAuraApp(..., { formatRegistry }); install only the formats the app uses.AuraModelFormatis open for that reason (B3). - Basis/KTX2 and Draco initialization now waits for the factory to settle before a decoder is published, and releases abandoned decoder instances before the disposal barrier settles.
@aura3d/assetsadds a./vite-clientsubpath next to./vitefor decoder wiring. ItssideEffectsfield now lists the KTX2 and Zstd worker modules (previouslyfalse), so bundlers keep those files.
5.6 Color pipeline
The default stays a linear working space with sRGB output. output.outputColorSpace may request display-p3. The active space can fall back to sRGB, and diagnostics() reports outputColorSpace as "srgb", "display-p3" or "unknown" (the device-observed value, not a claim about the physical display). output.alphaMode defaults to opaque. Alpha-preserving output also needs an explicit inputAlphaMode warranty (packages/engine/src/contracts/output.ts). @aura3d/rendering adds managed color conversion helpers (convertManagedColor, convertColorPrimaries, transformColorForOutput, and others).
5.7 Camera depth
camera.depth: { mode: "standard" | "reversed" | "logarithmic", allowFallback? } is new. All three modes render natively in the production renderer; the nonstandard modes are experimental (source and mock-device tested, no real-GPU evidence yet). Every mode is authored with a minus-one-to-one camera projection and requires a finite far (packages/engine/src/agent-api/compiler/cameraDepth.ts); the renderer rebuilds a reversed projection in zero-to-one form where the device rasterizes it.
- Reversed. The projection maps near to depth 1 and far to depth 0; the frame clears depth to 0 and compares
greater-equal. MaterialdepthCompareand polygon offsets stay authored in standard terms; the renderer and devices flip them for the frame. On WebGL2 withEXT_clip_controlthe frame rasterizes a zero-to-one reversed projection into float depth (DEPTH32F_STENCIL8) andrenderer.getDepthConvention()/diagnostics().renderer.depthreportprecision: "full". Without the extension, on WebGPU (its programs remap the minus-one-to-one projection in the vertex stage) or when the frame draws straight into a fixed-point canvas or caller target, ordering is correct andprecisionis"reduced"with aprecisionReason. Render targets acceptdepthPrecision: "float"(WebGPU:depth32float, ordepth32float-stencil8when the device was created with that feature, elsedepth24plus-stencil8). - Logarithmic. The projection is the standard one; generated programs write
ln(distance / near) / ln(far / near)as fragment depth, which disables early depth testing for those draws. Requires finite0 < near < far. Generated WebGL2 and WebGPU programs both write it (WGSL@builtin(frag_depth), same formula, same view and near/far data). Custom or legacy shaders that cannot write logarithmic depth fail withFORWARD_LOG_DEPTH_PROGRAM_UNSUPPORTED. - What follows the convention. Clears, forward and VFX draw compares, camera depth prepasses (outlines), postprocess depth linearization (AO, DoF, SSR, motion blur, fog, god rays), scene depth copies and CPU depth readback. Shadow maps, probe faces and mirrors keep standard depth for their own projections. Picking and scene queries use
app.viewProjection, which is always the standard-depth matrix (x/y is identical in every mode); passcamera.depth.modetoraycastwhen you supply a reversed matrix yourself. - Precise failures. Contact shadows, Gaussian splats, scalar volumes, depth preview materials and the native deferred/meshlet/visibility paths require standard depth (or use the forward renderer). Weighted transparency falls back to sorted transparency. The standalone legacy
VolumeRenderer(draw({ depth }), default the owning device's active convention) and vegetation impostors (bindimpostorDepthUniforms(convention)) write fragment depth in the frame convention.
Before 5.0 the same request threw DEPTH_MODE_UNSUPPORTED, or with allowFallback: true rendered standard depth.
6. New opt-in features (experimental)
Every item here is experimental in 5.0.0. You must opt in, the documented fallback applies, and browser, GPU and device evidence is still pending (claims and boundaries). A source export or mock-device test does not establish rendered or hardware support.
| Feature | How to opt in | Fallback when unsupported |
|---|---|---|
| WebGPU backend | renderer: { backend: "webgpu" | "auto", allowFallback?, requiredFeatures? } | auto falls back to WebGL2 by default. webgpu fails unless allowFallback: true. diagnostics().renderer.backendSelection reports requested, active and fallbackReason. |
| Native WebGPU drawing paths (deferred, meshlet, visibility buffer) | renderer: { drawingPath: { mode: "deferred" | "meshlet" | "visibility", allowFallback } } | Unsupported features (textures, shadows, fog, local probes, instancing, transparency, wetness with a puddle-noise texture, among others) use the forward renderer when allowFallback is true; otherwise the frame fails. |
| Worker-owned rendering | createAuraApp(canvas, { worker: { createWorker, fallback: "main-thread" } }), or createWorkerRenderer from @aura3d/engine/worker (worker renderer) | Without fallback: "main-thread", an unsupported worker is an error. app.execution reports the active mode and fallbackReason. |
| Compute (WGSL) | app.compute.bind(webgpuDevice) | Every call throws compute-requires-webgpu until a WebGPU device is bound. WebGL2 cannot dispatch compute. |
| Nonstandard camera depth | camera.depth: { mode: "reversed" | "logarithmic" } | Rendered natively; unsupported combinations fail precisely (section 5.7). |
| Wide-gamut output | output: { outputColorSpace: "display-p3" } | Active space falls back to sRGB and is reported in diagnostics. |
| Photometric (IES) lights and projected spot images | photometricProfile on point/spot lights with renderer.photometricProfiles: true; renderer.projectedSpotImages (both default off) | Unsupported combinations report a degradation and use scalar lighting; strict mode rejects the draw (appendix). |
| Gaussian splats and scalar volumes | gaussianSplat(...), scalarVolume(...); @aura3d/rendering/volumetrics | gaussianSplatBackendDiagnostic reports backend limits. |
| Clipping planes | clipping, AuraClipGroup | Invalid planes are rejected; see ClippingPlanes limits. |
| New formats | @aura3d/assets/importers (PLY, STL, 3MF, IES, Gaussian splat), @aura3d/assets/exporters (OBJ, STL, render-target EXR) | Exports report omitted or lossy features as warnings. |
| Shader graph | @aura3d/rendering/shader-graph, @aura3d/engine/shader-graph-runtime | Compile diagnostics; WebGL cannot upload recursive data uniforms. |
| Add-ons package | @aura3d/addons (SpatialDOM/Vector renderers, stereo/ASCII/outline outputs, marching cubes, generators, sculpt/tube/fracture tools, WorkerPool) | Independent, optional package. |
| Staged v5 entry | @aura3d/engine/v5, @aura3d/engine/v5/game; call await preloadPhysics() before synchronous physics construction | The 4.x-compatible root keeps synchronous physics. Do not import @aura3d/engine/v5/internal or /v5/context; they are internal. |
"normal" custom post-pass input | Add "normal" to inputs of an HDR custom pass (effects.shaderPass({ inputs: ["normal"], ... }), AuraCustomPostPass.inputs); the shader reads u_normal (unit view-space normal in .xyz, coverage in .a), reconstructed once per view and frame from linear depth | WebGL2 only. Native WebGPU custom passes refuse it with POSTPROCESS_INPUT_UNAVAILABLE:<pass>:normal (postprocess). |
| Other new subpaths | @aura3d/engine/{scatter,worker,lighting,geometry-tools,v5/math/hull}, @aura3d/rendering/lighting, @aura3d/debug/helpers; @aura3d/engine/math gains a require (CJS) condition | Additive. |
AuraAssetRefIdentity<TType, TId> is a new public type of the root: the kind, id, type and brand fields of an asset ref, so AuraAssetRef is now AuraAssetDefinition & AuraAssetRefIdentity (the same structure as 4.x). A project compiled with declaration: true can now export an inferred value that holds assets.x refs (for example export const bundle = { hero: assets.hero }); in 4.x the emitted type had to name the engine's private brand symbol and failed with TS4023, which forced an explicit type annotation. Refs still come only from defineAuraAssets; the type does not let you construct one.
bindPointerInteractions(app, listener) is a new public export of the root and the staged @aura3d/engine/v5 entry: it routes canvas pointer events to interactions.pointer({ target }) nodes, firing a targeted interaction only when its node is the nearest hit, and returns a disposer.
Hinge joint limits are relative to the creation pose
In 5.0 hinge limits are measured from the pose the joint was created in (both on the Rapier backend and the native solver). In 4.x they were absolute angles. If a hinged body is created already rotated (for example a flipper at rest angle rest that swings to up), change limits: [rest, up] to limits: [0, up - rest]. Bodies created at their zero pose are unaffected.
lookAt on models and primitives is now applied
In 4.x the render path silently ignored lookAt on models and primitives (only Gaussian splats honoured it). In 5.0 the model matrix applies it, composed with any pivot (the pivot point stays fixed and -Z faces the target). If a 4.x scene set lookAt on a model or primitive and relied on it being ignored, remove it. Camera lookAt is unchanged. (No in-repo app or template used model/primitive lookAt.)
7. Removed deprecated items (B14, B15)
These were deprecated in 4.x and removed in 5.0.0 (breaking changes B14 and B15).
Removed @aura3d/engine subpaths (B15):
| Subpath | Use instead |
|---|---|
./production-runtime, ./advanced-runtime | @aura3d/engine/renderer (it re-exports the production-runtime SDK) |
./rendering, ./rendering/webgpu, ./rendering/production-runtime | @aura3d/engine/renderer for the renderer, geometry, materials and textures; the full low-level surface is the published @aura3d/rendering package (@aura3d/rendering/webgpu, @aura3d/rendering/production-runtime) |
./assets/browser | @aura3d/engine/assets (or @aura3d/assets/browser) |
./workflows/production | @aura3d/engine/workflows (runProductionExample) |
./contracts | @aura3d/engine (root); createGame from @aura3d/engine/game (B1); AuraCameraProbe from @aura3d/engine/devtools |
./apps | @aura3d/apps (A3DApp, quality presets); createAuraApp for new apps |
./media-node | Not removed: it stays the Node-only media entry (B15). |
Removed root values, grouped by replacement (B14):
| Use instead | Removed root names |
|---|---|
createAuraApp | createA3DApp |
@aura3d/engine/renderer | A3DRenderer, Renderer, createGameAppRuntime |
@aura3d/engine/animation (or @aura3d/animation) | AnimationClip, AnimationLayer, AnimationMixer, AnimationTrack |
@aura3d/engine/assets (or @aura3d/assets) | GLTFLoader, inspectGLTFAsset, loadRenderableAsset |
@aura3d/engine/devtools | captureAuraAppScreenshot, createAssetCompatibilityReport, gameAssetValidation, quaterniusGameReadyFighterValidationContract, validateQuaterniusGameReadyFighterAsset |
@aura3d/engine/workflows (or @aura3d/workflows) | createAssetViewerWorkflow, createInteractiveSceneWorkflow, createProductConfiguratorWorkflow |
typed model(assets.x) | unsafeModelUrl |
| no replacement (internal helpers) | A3DAppLifecycle, captureScreenshot, compositeMetallicRoughnessPixels, createExternalParityEnvironmentPipeline, createProductionPrimitiveTextureIntent, crowds, describeProductionSpotShadow, inspectAsset, listExternalParityEnvironmentTargets, loadAsset, meshVehicleSurface, mipChainBytesCoarseToFine, normalizeSceneSnapshot, normalizeTextureBudgetBytes, resolveProductionRuntimeShadowTuning, resolveProductionShadowCasterIndex, summarizeExternalParityGLTFCorpus, upgradeProductionPrimitiveTextures |
Under the stability policy, nothing first deprecated in 5.x was removed in 5.0.0.
8. Checklist
- All
@aura3d/*packages on the same 5.0.x version; lockfile refreshed. - No
createGameimported from"@aura3d/engine"; game code uses@aura3d/engine/game. - Mocks and adapters of
AuraApp/AuraRuntimeNodeHandle/AuraRuntimeNodeSnapshotimplement the new members. - Exhaustive switches over
outputColorSpace,AuraAssetType,AuraModelFormatand materialkindhave a default branch. -
renderer.backendiswebgl2,webgpuorauto;experimental-webgpuprofile removed or paired with an explicit fallback choice. - Strict-mount error handling branches on
error.code, or usesreadiness()/readyStrict(). - Credentialed asset loads set
assets.securityContextif they need cache reuse; images fit the 64 MiB / 32 Mi-pixel limits. - React:
onErroror an error boundary aroundAuraCanvas; custom shared loaders pass asecurityContext. - No imports of the removed root values (B14) or legacy subpaths (B15); see section 7.
- Experimental paths (WebGPU, workers, depth, P3, compute) opted into only with a fallback you have tested.
-
npm run build && npx @aura3d/cli@latest check-deploy --dist distpasses.
9. Verify before relying on this guide
These items are uncertain in the current source and must be confirmed against the packed 5.0.0 release:
- Packed declarations. Every manifest reads 5.0.0, but the breaking-change list reflects source, not the packed declarations that the stability policy requires comparing.
- Camera depth. Reversed and logarithmic depth now render natively (section 5.7) without real-GPU evidence. Reversed precision is full on WebGL2 with
EXT_clip_control; WebGPU logarithmic depth renders natively. Open: full reversed precision on WebGPU (programs must consume a native zero-to-one projection), infinite far (decision record, item 3). - WebGPU teardown. A named 5.0 defect. Teardown behavior on disposal (
whenDisposed()) may change. - WebGPU wetness (experimental).
atmosphere.setWetnessnow renders wet surfaces on the native WebGPU forward program and on the deferred, meshlet and visibility paths, with the same response as WebGL2 (darker albedo, smoother puddle roughness, snow cover). It has no real-GPU evidence yet. A puddle-noise texture is not supported on WebGPU. - Pointer, XR and input types.
PointerSnapshot,GamepadSnapshotand the XR types gained exports. Whether any existing interface gained a required member was not audited for this guide. - Custom geometry attribute types.
AuraCustomGeometrySpec.attributesandmorphTargetschanged type (AuraCustomVertexAttributeSpec,AuraCustomVertexAttributeData). Confirm that 4.x literal inputs still type-check. - Installed behavior. Every behavior in sections 5 and 6 is source- or mock-tested only. Browser, GPU, device and packed-consumer results are pending acceptance.