AuraJS 3D API Contract
Public handbook: docs/external/game-dev-api/README.md
Exact frozen signatures, validation rules, and reason-code semantics remain in this file. This file replaces the old api-contract-3d-v2.md path.
Table of Contents
- Animation Mixer and Crossfade
- Physics3D Joint Integration
- Terrain Runtime Integration
- Conventions
aura.draw3d
aura.camera3d
aura.light
aura.mesh
aura.material
aura.compute
- Error Semantics Summary
- Reference Examples
- Frozen Surface Summary
This document defines the exact public 3D API surface:
aura.draw3d
aura.camera3d
aura.light
aura.mesh
aura.material
aura.compute
It freezes JS-visible signatures, argument validation behavior, and error semantics so renderer and binding work can proceed without drift.
Related 3D gameplay physics note:
- The public
aura.physics3d contract, including rigid bodies, queries, and
joints, is defined in docs/api-contract.md.
- This file still documents the 3D-side integration expectations relevant to
authored scenes, materials, and rendering.
- Native
.mp4 video playback for in-world screens remains defined separately
in docs/native-mp4-video-contract-v1.md.
Split Routes
Use one of these narrower exact-reference pages before reading this file
end-to-end:
- Draw3D, Camera, Lighting, and PostFX Exact Reference
- Scene3D, Mesh, Material, glTF, and Avatar Exact Reference
- Physics3D, Terrain, Navmesh, Character3D, and Compute Exact Reference
- Net, Multiplayer, and Runtime Sessions Exact Reference
0.5 Animation Mixer and Crossfade
Deterministic clip blending is provided through
aura.animation (documented in the core contract addendum):
- Use
aura.animation.crossfade(timelineId, { duration, fromTime?, toTime?, eventTag? })
for mixer-style clip blend orchestration.
crossfade(...) emits deterministic crossfade_complete event callbacks in the same
ordering phase used by timeline transitions.
- Transition/marker payloads include
blendMode ("crossfade" or "transition") so
gameplay logic can branch deterministically without per-frame polling.
pause/resume/seek behavior with active blends remains deterministic and stable.
0.6 Physics3D Joint Integration
Public 3D mechanism behavior is now expected to use the documented
aura.physics3d surface rather than gameplay-specific proxies:
- canonical joint kinds are
revolute, ball, prismatic, and fixed
- public joint views expose stable
id, kind, bodyA, bodyB, getState(),
remove(), and state
Truthful integration scope:
- use
aura.physics3d to author visible mechanisms such as hinged doors,
slider platforms, pendulums, and locked mounts inside a rendered 3D scene
- joint-backed gameplay is part of the supported 3D gameplay surface
- non-goals remain unchanged: this does not imply cloth, soft-body, vehicles,
or breakable-constraint authoring
Minimal example:
const hinge = aura.physics3d.joint("revolute", frame.id, door.id, {
anchorA: { x: 0.5, y: 0, z: 0 },
anchorB: { x: -0.5, y: 0, z: 0 },
axis: { x: 0, y: 1, z: 0 },
limits: { min: -0.8, max: 0.8 },
motor: { targetVel: 1.0, maxForce: 40 }
});
const hingeState = hinge.getState();
Use the canonical kind names above in authored game code and docs. Legacy alias
strings are compatibility-only normalization inputs, not the public contract.
0.7 Terrain Runtime Integration
Public terrain gameplay now uses the canonical aura.terrain surface rather
than older helper-only terrain submission hooks:
- terrain creation and mutation live on
aura.terrain.create(...),
setSplatMap(...), setLayerTexture(...), setHeightmap(...), and
setVisible(...)
- stable runtime inspection lives on
aura.terrain.getInfo(...),
getVegetationInfo(...), getSubmissionState(), and sampleHeight(...)
- vegetation scatter uses
aura.terrain.addVegetation(...) /
removeVegetation(...) on the same canonical terrain handle
Truthful scope:
- the public terrain runtime supports real native heightfield rendering,
texture splat layering, vegetation submission, and deterministic runtime
telemetry
- the public surface is appropriate for authored runtime terrain slices and
gameplay-driven terrain queries
- non-goals remain unchanged: this does not imply a terrain editor, biome
painter, chunk streaming, or vegetation placement tooling
Minimal example:
const terrainId = aura.terrain.create({
width: 16,
depth: 16,
segmentsX: 6,
segmentsZ: 6,
maxHeight: 5
});
aura.terrain.setSplatMap(terrainId, "assets/terrain-splat.png");
aura.terrain.setLayerTexture(terrainId, 0, "assets/terrain-grass.png", 4);
aura.terrain.setHeightmap(terrainId, heightmapValues);
const vegetationId = aura.terrain.addVegetation(terrainId, {
texture: "assets/terrain-grass-blade.png",
density: 0.45,
minHeight: 0.7,
maxHeight: 1.35,
splatChannel: 0
});
const terrainState = aura.terrain.getSubmissionState();
1) Conventions (must match the core contract)
- Coordinates: Right-handed world space.
+X right, +Y up, +Z forward.
- Angles: Radians.
- Color format: Aura v1 color convention (
{r,g,b,a} float channels in [0.0, 1.0]; a defaults to 1.0 where applicable).
- Handle style: Opaque numeric handles for mesh/material/light resources.
- Transform inputs:
{ position?: Vec3, rotation?: Vec3, scale?: Vec3 } where rotation is Euler radians (x, y, z).
Type aliases (TypeScript notation):
type MeshHandle = number;
type MaterialHandle = number;
type LightHandle = number;
type DataTextureHandle = number;
type Vec3 = { x: number; y: number; z: number };
type Color = { r: number; g: number; b: number; a?: number };
type ColorRgb = { r: number; g: number; b: number };
type Mat4 = [
number, number, number, number,
number, number, number, number,
number, number, number, number,
number, number, number, number
];
2) `aura.draw3d`
2.0 `aura.draw3d.createMesh(vertices, indices)`
aura.draw3d.createMesh(
vertices: Array<{
position: [number, number, number];
normal?: [number, number, number];
uv?: [number, number];
color?: [number, number, number, number];
jointIndices?: [number, number, number, number];
jointWeights?: [number, number, number, number];
}>,
indices: number[]
): MeshHandle
| Property |
Value |
| Description |
Create a JS-authored mesh handle for the legacy aura.draw3d queue surface. |
| Validation |
vertices must be an array of objects and indices must be an array of numbers. jointIndices, when provided, must be exactly 4 finite non-negative integers in u32 range. jointWeights, when provided, must be exactly 4 finite non-negative numbers with a strictly positive sum. |
| Normalization |
Missing normal, uv, and color still fall back to the existing defaults. Missing jointIndices explicitly fall back to [0, 0, 0, 0]. Missing jointWeights explicitly fall back to [1, 0, 0, 0]. Provided jointWeights are normalized deterministically to sum to 1.0. |
| Error behavior |
Malformed skinning arrays throw deterministic JS errors with stable reason-code fragments: invalid_skinning_joint_indices, invalid_skinning_joint_index_value, invalid_skinning_joint_weights, invalid_skinning_joint_weight_value, or invalid_skinning_joint_weight_total. |
| Scope note |
This contract covers JS-authored mesh input only. glTF mesh ingest remains unchanged in this task. |
aura.draw3d.drawMesh(mesh: MeshHandle, material: MaterialHandle, transform?: {
position?: Vec3;
rotation?: Vec3;
scale?: Vec3;
}): void
| Property |
Value |
| Description |
Queue a mesh draw for the current frame using the provided material and transform. |
| Validation |
Invalid mesh/material handle -> warning + no-op. Invalid transform fields -> warning + field fallback (position={0,0,0}, rotation={0,0,0}, scale={1,1,1}). |
| Error behavior |
Never throws for validation failures; warning + no-op/fallback. |
aura.draw3d.drawMeshInstanced(
mesh: MeshHandle,
material: MaterialHandle,
transforms: Array<{
position?: Vec3;
rotation?: Vec3;
scale?: Vec3;
}>
): void
| Property |
Value |
| Description |
Queue a contiguous instanced mesh submission for the current frame. |
| Validation |
Invalid mesh/material handle -> warning + no-op. transforms must be a non-empty array. Invalid transform fields use the same per-field fallbacks as drawMesh. |
| Error behavior |
Never throws for validation failures; warning + no-op/fallback. |
2.3 `aura.draw3d.drawSkybox(path)`
aura.draw3d.drawSkybox(path: string): void
| Property |
Value |
| Description |
Set or draw skybox from path (cubemap directory or equirect source per renderer support). |
| Validation |
Non-string/empty path -> warning + no-op. |
| Error behavior |
Missing/invalid asset path -> throws Error("Asset not found: <path>"). |
2.4 `aura.draw3d.clear3d(color?)`
aura.draw3d.clear3d(color?: Color): void
| Property |
Value |
| Description |
Clear 3D background/depth for the frame before mesh draws. |
| Validation |
Missing/invalid color -> warning, fallback to default { r: 0.08, g: 0.08, b: 0.12, a: 1.0 }. |
| Error behavior |
Never throws on invalid color input. |
2.4a `aura.draw3d.setFog(options)`
aura.draw3d.setFog(options: {
mode: "linear" | "exp" | "exp2";
color?: Color | ColorRgb;
near?: number;
far?: number;
density?: number;
atmosphere?: {
enabled?: boolean;
baseY?: number;
falloff?: number;
rayStrength?: number;
rayDecay?: number;
rayExposure?: number;
};
}): void
| Property |
Value |
| Description |
Configure retained global fog for the forward 3D pass. |
| Validation |
options must be an object. mode is required and must be one of "linear", "exp", or "exp2". near clamps to >= 0. For "linear", far is sanitized to stay strictly greater than near. density clamps to >= 0. When atmosphere is present, falloff clamps to >= 0.1, rayStrength clamps to [0, 1], rayDecay clamps to [0.05, 0.99], and rayExposure clamps to [0, 1]. |
| Truth note |
atmosphere enables one bounded Atmosphere V1 overlay on top of retained fog: height-aware fog plus one renderer-owned screen-space shaft lane driven by the current directional light. It is perspective-camera-only, global, and full-frame. It does not promise local fog volumes, per-object exclusion, true volumetric scattering, cube-camera capture, or render-target-capture support. |
| Error behavior |
Invalid input warns and leaves the current retained fog state unchanged. Never throws. |
2.4b `aura.draw3d.clearFog()`
aura.draw3d.clearFog(): void
| Property |
Value |
| Description |
Clear the retained global fog configuration and any coupled Atmosphere V1 overlay. |
| Error behavior |
Never throws. |
2.5 `aura.draw3d.billboard(textureHandleOrSource, options)`
aura.draw3d.billboard(
textureHandleOrSource:
| number
| {
dataTextureHandle: number;
},
options: {
x: number;
y: number;
z: number;
width: number;
height: number;
mode?: "face" | "axis";
color?: Color;
frameX?: number;
frameY?: number;
frameW?: number;
frameH?: number;
atlasWidth?: number;
atlasHeight?: number;
}
): void
| Property |
Value |
| Description |
Queue a camera-facing billboard quad. Existing numeric handles remain the material/video lane. Procedural data textures use the bridge object form { dataTextureHandle } so overlapping handle spaces stay unambiguous. |
| Validation |
textureHandleOrSource must be a positive integer or an object with positive-integer dataTextureHandle. options must be an object. x, y, z, width, and height must be finite, with width > 0 and height > 0. mode accepts "face" or "axis" and otherwise falls back to "face" with a warning. color, when provided, must be color-like or it falls back to opaque white. Atlas-frame selection requires frameX, frameY, frameW, frameH, atlasWidth, and atlasHeight together as finite numbers inside the atlas bounds. |
| Error behavior |
Never throws for validation failures; warning + no-op. |
2.6 `aura.draw3d.setPostFXPass(pass, options?)`
aura.draw3d.setPostFXPass(
pass: string,
options?: {
enabled?: boolean;
strength?: number;
radius?: number;
threshold?: number;
customParams?: Record<string, number>;
targetChain?: {
intermediateTargets?: string[];
pingPong?: boolean;
composeMode?: "replace" | "additive" | "multiply";
};
}
): {
ok: boolean;
operation: "setPostFXPass";
reasonCode: string;
pass: string | null;
}
| Property |
Value |
| Description |
Add or update one deterministic postfx pass in the retained composer state. |
| Validation |
pass must be a supported builtin pass name or custom:<symbol>. options, when provided, must be an object. enabled must be boolean. strength, radius, and threshold must be finite numbers and are clamped to deterministic per-pass ranges. customParams must be an object with symbol-like keys and finite numeric values. targetChain, when provided, must be an object with string intermediateTargets, boolean pingPong, and compose mode in the supported set. |
| Error behavior |
Never throws for validation failures; returns { ok: false, reasonCode, pass }. Stable reason codes include postfx_invalid_pass_name, postfx_pass_unsupported, postfx_invalid_options, postfx_invalid_target_chain, postfx_invalid_intermediate_target, postfx_invalid_compose_mode, postfx_invalid_ping_pong, postfx_invalid_custom_params, and postfx_custom_param_unsupported. |
2.7 `aura.draw3d.setPostFXEnabled(pass, enabled)`
aura.draw3d.setPostFXEnabled(
pass: string,
enabled: boolean
): {
ok: boolean;
operation: "setPostFXEnabled";
reasonCode: string;
pass: string | null;
}
| Property |
Value |
| Description |
Toggle one previously configured postfx pass on or off without removing it from retained composer state. |
| Validation |
pass must resolve to a supported builtin pass or custom:<symbol>. enabled must be a boolean. |
| Error behavior |
Never throws for validation failures; returns { ok: false, reasonCode, pass }. Stable reason codes include postfx_invalid_pass_name, postfx_pass_unsupported, postfx_invalid_enabled, and postfx_pass_missing. |
2.8 `aura.draw3d.removePostFXPass(pass)`
aura.draw3d.removePostFXPass(pass: string): {
ok: boolean;
operation: "removePostFXPass";
reasonCode: string;
pass: string | null;
}
| Property |
Value |
| Description |
Remove one configured postfx pass from retained composer state. |
| Validation |
pass must resolve to a supported builtin pass or custom:<symbol>. |
| Error behavior |
Never throws for validation failures; returns { ok: false, reasonCode, pass }. Stable reason codes include postfx_invalid_pass_name, postfx_pass_unsupported, and postfx_pass_missing. |
2.9 `aura.draw3d.getPostFXState()`
aura.draw3d.getPostFXState(): {
passes: Array<{
pass: string;
enabled: boolean;
strength: number;
radius: number;
threshold: number;
customParams: Record<string, number>;
index: number;
isCustom: boolean;
}>;
resolvedSteps: Array<{
pass: string;
targetSlot: string | null;
pingPongPhase: boolean;
usesShaderPipeline: boolean;
inputMatchesOriginalScene: boolean;
index: number;
}>;
targetChain: {
intermediateTargets: string[];
intermediateTargetCount: number;
pingPong: boolean;
composeMode: "replace" | "additive" | "multiply";
};
customShaderBindings: Array<{
binding: number;
name: string;
type: string;
semantic: string;
}>;
customShaderContract: {
deterministicOrder: boolean;
supportsDepthTexture: boolean;
supportsOriginalSceneTexture: boolean;
supportsCameraInfoInUniform: boolean;
supportsNormalBuffer: boolean;
supportsIntermediateBufferReads: boolean;
customParamSlots: number;
inputTextureSemantic: string;
originalSceneSemantic: string;
orderingSemantic: string;
};
totalPasses: number;
enabledPasses: number;
mutationCount: number;
orderFingerprint: number;
targetChainFingerprint: number;
customPassCount: number;
customParamFingerprint: number;
lastOperation: string;
lastReasonCode: string;
lastOk: boolean;
}
| Property |
Value |
| Description |
Return deterministic retained composer telemetry for tools, inspectors, and runtime assertions. |
| Truth note |
resolvedSteps reflects the deterministic runtime execution order, including target-chain expansion. customShaderBindings and customShaderContract describe the public custom-postfx input surface. |
| Scope note |
input_texture is the current scene color immediately before a custom pass. original_scene_texture is a stable scene-color copy from before the postfx chain began. depth_texture is the current swapchain 3D depth surface. Normal-buffer / G-buffer reads and arbitrary intermediate-buffer reads remain unsupported. |
2.10 `aura.draw3d.registerPostFXShader(name, wgslSource)`
aura.draw3d.registerPostFXShader(
name: string,
wgslSource: string
): {
ok: boolean;
name: string;
error?: string;
}
| Property |
Value |
| Description |
Queue a custom WGSL postfx shader for renderer compilation. The canonical custom pass name is always custom:<name>. Re-registering replaces the pending/compiled shader for that canonical name. |
| Validation |
name must be a non-empty symbol-like string not reserved by builtin passes. wgslSource must be a non-empty string and contain fn fs_main. |
| Error behavior |
Validation failures do not throw; they return { ok: false, name, error }. Native compilation happens later in the renderer and is surfaced through runtime compile-result tracking rather than synchronously from registration. |
2.11 PostFX custom-shader input contract
aura.draw3d.registerPostFXShader(name, wgslSource) and
aura.draw3d.setPostFXPass("custom:name", options?) now rely on one explicit
custom-shader input surface:
input_texture is the current scene color immediately before this custom
pass
original_scene_texture is a stable scene-color copy from before the postfx
chain started
depth_texture is the current swapchain 3D depth texture
u_postfx includes resolution, texel size, time, 8 custom param slots,
camera near/far, projection-mode flag, and step/input-contract flags
Deterministic truth constraints:
aura.draw3d.getPostFXState() exposes resolvedSteps,
customShaderBindings, and customShaderContract so tooling can inspect the
actual runtime ordering and binding map
- normal-buffer / G-buffer reads remain unsupported
- arbitrary intermediate-buffer reads remain unsupported
- custom shaders are still single-pass effects within the existing deterministic
postfx chain rather than a full user-defined render graph
3) `aura.camera3d`
3.1 `aura.camera3d.perspective(fovDeg, near, far)`
aura.camera3d.perspective(fovDeg: number, near: number, far: number): void
| Property |
Value |
| Validation |
Non-number args -> warning + no-op. fovDeg clamped to [1, 179]. near <= 0 or far <= near -> warning + no-op. |
| Error behavior |
Never throws for invalid arguments. |
3.2 `aura.camera3d.lookAt(x, y, z)`
aura.camera3d.lookAt(x: number, y: number, z: number): void
| Property |
Value |
| Validation |
Non-number args -> warning + no-op. |
| Error behavior |
Never throws. |
3.3 `aura.camera3d.setPosition(x, y, z)`
aura.camera3d.setPosition(x: number, y: number, z: number): void
3.4 `aura.camera3d.setTarget(x, y, z)`
aura.camera3d.setTarget(x: number, y: number, z: number): void
3.5 `aura.camera3d.setFOV(fovDeg)`
aura.camera3d.setFOV(fovDeg: number): void
| Property |
Value |
| Validation |
Non-number input -> warning + no-op. Value clamped to [1, 179] with warning. |
| Error behavior |
Never throws. |
3.6 `aura.camera3d.getViewMatrix()`
aura.camera3d.getViewMatrix(): Mat4
3.7 `aura.camera3d.getProjectionMatrix()`
aura.camera3d.getProjectionMatrix(): Mat4
| Property |
Value |
| Return value |
16-number matrix (column-major) suitable for shader uniform upload. |
| Error behavior |
Never throws. Returns last valid matrix state. |
3.8 `aura.camera3d.setControlProfile(profile, options?)`
aura.camera3d.setControlProfile(
profile: "none" | "orbit",
options?: {
rotateSpeed?: number;
panSpeed?: number;
zoomSpeed?: number;
damping?: number; // clamped to [0, 1]
minDistance?: number; // > 0, clamped to deterministic safe range
maxDistance?: number; // > 0, clamped to deterministic safe range
minPitchDeg?: number; // clamped to [-89, 89]
maxPitchDeg?: number; // clamped to [-89, 89]
}
): void
| Property |
Value |
| Validation |
Non-string/unsupported profile -> warning + no-op. Non-object options -> warning + no-op. Invalid numeric fields -> warning + no-op. Range values clamp where documented. |
| Error behavior |
Never throws. |
aura.camera3d.updateControls(
dtSeconds: number,
input?: {
rotateX?: number;
rotateY?: number;
panX?: number;
panY?: number;
zoom?: number;
}
): void
| Property |
Value |
| Validation |
dtSeconds must be finite and >= 0. Non-object input or invalid numeric fields -> warning + no-op. Input deltas are clamped to deterministic bounds. |
| Error behavior |
Never throws. No-op when no active control profile is enabled. |
3.10 `aura.camera3d.getControlState()`
aura.camera3d.getControlState(): {
profile: "none" | "orbit";
active: boolean;
orbit: {
yaw: number;
pitch: number;
distance: number;
target: Vec3;
rotateSpeed: number;
panSpeed: number;
zoomSpeed: number;
damping: number;
minDistance: number;
maxDistance: number;
minPitchDeg: number;
maxPitchDeg: number;
};
}
| Property |
Value |
| Return value |
Deterministic snapshot of current control profile + orbit state. |
| Error behavior |
Never throws. |
4) `aura.light`
4.1 `aura.light.ambient(color, intensity)`
aura.light.ambient(color: Color, intensity: number): LightHandle
4.2 `aura.light.hemisphere(skyColor, groundColor, intensity, upDirection?)`
aura.light.hemisphere(
skyColor: Color,
groundColor: Color,
intensity: number,
upDirection?: Vec3
): LightHandle
| Property |
Value |
| Description |
Set the singleton sky-ground ambient split used for outdoor daylight readability. |
| Return value |
Returns the ambient/light-environment sentinel handle (0). |
| Validation |
skyColor and groundColor are required. Structurally invalid required args throw TypeError. Invalid color payloads warn and fall back deterministically (skyColor -> white, groundColor -> black). intensity clamps to >= 0. Missing/invalid upDirection falls back to { x: 0, y: 1, z: 0 }. |
| Scope note |
This is a deterministic hemisphere/skylight term, not propagated block-light or full GI. |
4.3 `aura.light.directional(direction, color, intensity)`
aura.light.directional(direction: Vec3, color: Color, intensity: number): LightHandle
4.4 `aura.light.point(position, color, intensity, range)`
aura.light.point(position: Vec3, color: Color, intensity: number, range: number): LightHandle
4.5 `aura.light.spot(position, direction, color, intensity, range, angleRadians)`
aura.light.spot(
position: Vec3,
direction: Vec3,
color: Color,
intensity: number,
range: number,
angleRadians: number
): LightHandle
4.6 `aura.light.update(handle, props)`
aura.light.update(handle: LightHandle, props: {
position?: Vec3;
direction?: Vec3;
color?: Color;
intensity?: number;
range?: number;
angle?: number;
angleRadians?: number;
skyColor?: Color;
groundColor?: Color;
hemisphereIntensity?: number;
skyIntensity?: number;
upDirection?: Vec3;
}): void
4.7 `aura.light.remove(handle)`
aura.light.remove(handle: LightHandle): void
| Property |
Value |
| Validation |
Invalid color/vector/intensity/range -> warning + no-op (or per-field fallback for update). Invalid handle on update/remove -> silent no-op. |
| Error behavior |
ambient/hemisphere/directional/point/spot throw TypeError only for structurally invalid required args (null/wrong primitive type). update/remove never throw on invalid handles. |
| Limits |
Runtime supports 1 directional + up to 8 point lights + up to 8 spot lights per frame; overflow logs warning and drops newest light command. |
| Singleton note |
update(0, props) updates the ambient singleton and, when skyColor, groundColor, hemisphereIntensity / skyIntensity, or upDirection are present, also updates the hemisphere singleton. remove(0) resets ambient to defaults and clears the hemisphere contribution. |
4.7 `aura.light.setShadowCasting(lightId, enabled)`
aura.light.setShadowCasting(lightId: LightHandle, enabled: boolean): void
4.8 `aura.light.setShadowQuality(lightId, quality)`
aura.light.setShadowQuality(
lightId: LightHandle,
quality: "low" | "medium" | "high"
): void
4.9 `aura.light.setShadowBudget(maxLights)`
aura.light.setShadowBudget(maxLights: number): void
aura.light.configureDirectionalShadows(options: {
enabled?: boolean;
quality?: "low" | "medium" | "high";
bias?: number;
normalBias?: number;
filterMode?: "hard" | "pcf";
filterRadius?: number;
cascadeCount?: number;
tileResolution?: number;
lambda?: number;
blendWidth?: number;
shadowFar?: number;
stabilizeCascades?: boolean;
}): void
aura.light.configureShadow(lightId: LightHandle, options: {
enabled?: boolean;
quality?: "low" | "medium" | "high";
bias?: number;
normalBias?: number;
filterMode?: "hard" | "pcf";
filterRadius?: number;
}): void
4.12 `aura.light.getShadowState(lightId?)`
aura.light.getShadowState(lightId?: LightHandle): {
shadowBudget: number;
shadowCastingCount: number;
budgetSaturated: boolean;
shadowedPointLightCount: number;
shadowedSpotLightCount: number;
rendererActive: boolean;
effectiveShadowCastingCount: number;
effectivePointLightCount: number;
effectiveSpotLightCount: number;
directionalPassCount: number;
multiLightSlotCount: number;
multiLightPassCount: number;
shadowDrawCount: number;
directional: {
enabled: boolean;
quality: "low" | "medium" | "high";
bias: number;
normalBias: number;
filterMode: "hard" | "pcf";
filterRadius: number;
cascadeCount: number;
tileResolution: number;
lambda: number;
blendWidth: number;
shadowFar: number;
stabilizeCascades: boolean;
};
} | {
lightId: LightHandle;
type: "directional" | "point" | "spot";
enabled: boolean;
quality: "low" | "medium" | "high";
bias: number;
normalBias: number;
filterMode: "hard" | "pcf";
filterRadius: number;
rendererActive: boolean;
effectiveEnabled: boolean;
effectiveQuality: "low" | "medium" | "high";
effectiveBias: number;
effectiveSlotCount: number;
} | null
4.13 `aura.light.getShadowStats()`
aura.light.getShadowStats(): {
shadowCastingCount: number;
shadowBudget: number;
budgetSaturated: boolean;
directionalShadowEnabled: boolean;
directionalQuality: "low" | "medium" | "high";
directionalBias: number;
directionalNormalBias: number;
directionalFilterMode: "hard" | "pcf";
directionalFilterRadius: number;
directionalCascadeCount: number;
directionalTileResolution: number;
directionalLambda: number;
directionalBlendWidth: number;
directionalShadowFar: number;
directionalStabilizeCascades: boolean;
directionalTexelSnapErrorMax: number;
shadowedPointLightCount: number;
shadowedSpotLightCount: number;
rendererActive: boolean;
effectiveShadowCastingCount: number;
effectivePointLightCount: number;
effectiveSpotLightCount: number;
directionalPassCount: number;
multiLightSlotCount: number;
multiLightPassCount: number;
shadowDrawCount: number;
}
| Property |
Value |
| Validation |
setShadowCasting / setShadowQuality / configureShadow require a valid existing light handle. Unknown handles log warning and no-op. configureDirectionalShadows and configureShadow require an options object and throw TypeError when missing. |
| Clamping |
shadowBudget clamps to 1..=4. Directional bias clamps to 0.0..=0.05, normalBias to 0.0..=0.1, filterRadius to 0.0..=4.0, cascadeCount to 2..=4, tileResolution to 64..=4096, lambda to 0.0..=1.0, blendWidth to 0.0..=50.0, and shadowFar to 10.0..=10000.0. Per-light bias clamps to 0.0..=0.05, normalBias to 0.0..=0.1, and filterRadius to 0.0..=4.0. Invalid shadow quality strings warn and fall back to "medium". Invalid filterMode strings warn and fall back to "pcf". stabilizeCascades uses JS boolean coercion when provided. |
| Runtime note |
These APIs define the JS/runtime control contract. configureDirectionalShadows() and configureShadow() feed renderer-owned effective shadow state, while getShadowState(), getShadowStats(), and aura.debug.inspectorStats().scene3dRuntime.shadow report the clamped/runtime-effective values that were actually consumed. directionalTexelSnapErrorMax is the stabilization metric for active directional cascades: values near 0.0 mean the live cascades are currently snapped to the texel grid after clamping. Use those surfaces as the truth source instead of relying on renderer internals. |
5) `aura.mesh`
5.1 `aura.mesh.load(path)`
aura.mesh.load(path: string): MeshHandle
5.2 `aura.mesh.createBox(width?, height?, depth?)`
aura.mesh.createBox(width?: number, height?: number, depth?: number): MeshHandle
5.3 `aura.mesh.createSphere(radius?, segments?)`
aura.mesh.createSphere(radius?: number, segments?: number): MeshHandle
5.4 `aura.mesh.createPlane(width?, depth?)`
aura.mesh.createPlane(width?: number, depth?: number): MeshHandle
5.5 `aura.mesh.createCylinder(radius?, height?, segments?)`
aura.mesh.createCylinder(radius?: number, height?: number, segments?: number): MeshHandle
5.6 `aura.mesh.createCone(radius?, height?, segments?)`
aura.mesh.createCone(radius?: number, height?: number, segments?: number): MeshHandle
5.7 `aura.mesh.createTorus(majorRadius?, minorRadius?, radialSegments?, tubularSegments?)`
aura.mesh.createTorus(
majorRadius?: number,
minorRadius?: number,
radialSegments?: number,
tubularSegments?: number
): MeshHandle
5.8 `aura.mesh.createCapsule(radius?, height?, segments?, rings?)`
aura.mesh.createCapsule(
radius?: number,
height?: number,
segments?: number,
rings?: number
): MeshHandle
5.9 `aura.mesh.createRing(innerRadius?, outerRadius?, thetaSegments?)`
aura.mesh.createRing(
innerRadius?: number,
outerRadius?: number,
thetaSegments?: number
): MeshHandle
| Property |
Value |
| Description |
Build a flat indexed ring mesh on the XZ plane. |
| Defaults |
innerRadius=0.5, outerRadius=1.0, thetaSegments=32. |
| Validation |
innerRadius must be finite and >= 0. outerRadius must be finite and strictly greater than innerRadius. thetaSegments must be a finite non-negative integer. Runtime mesh generation clamps thetaSegments to at least 3. |
| Error behavior |
Throws Error for malformed numeric input or invalid radius ordering. |
| Usage note |
Use this for small procedural slices, decals, portals, and simple parametric props. Prefer authored assets when the shape needs bespoke topology or sculpted wear. |
5.10 `aura.mesh.createExtrude(shape2d, optionsOrDepth?, segments?)`
aura.mesh.createExtrude(
shape2d: Array<{ x: number; y: number }>,
optionsOrDepth?: { depth?: number; segments?: number } | number,
segments?: number
): MeshHandle
| Property |
Value |
| Description |
Extrude a closed 2D polygon into indexed 3D geometry. |
| Defaults |
depth=1.0, segments=1. |
| Validation |
shape2d must be an array of at least 3 {x, y} points with finite coordinates. depth must be finite and > 0. segments must be a finite non-negative integer and runtime clamps it to at least 1. If you pass an options object, do not also pass trailing positional depth/segments. |
| Error behavior |
Throws Error for malformed points, invalid depth, or mixed options + positional arguments. |
| Usage note |
Preferred form is the options object because it freezes the authored meaning of depth and segments directly in call sites. |
5.11 `aura.mesh.createLathe(points, optionsOrSegments?, phiStart?, phiLength?)`
aura.mesh.createLathe(
points: Array<{ x: number; y: number }>,
optionsOrSegments?: {
segments?: number;
phiStart?: number;
phiLength?: number;
} | number,
phiStart?: number,
phiLength?: number
): MeshHandle
| Property |
Value |
| Description |
Revolve a 2D profile around the Y axis to create indexed lathe geometry. |
| Defaults |
segments=12, phiStart=0, phiLength=TAU. |
| Validation |
points must be an array of at least 2 {x, y} points with finite coordinates. Profile x values must stay >= 0. segments must be a finite non-negative integer and runtime clamps it to at least 3. phiStart must be finite. phiLength must be finite and > 0. If you pass an options object, do not also pass trailing positional segments/phiStart/phiLength. |
| Error behavior |
Throws Error for malformed points, negative profile radii, non-positive phiLength, or mixed options + positional arguments. |
| Usage note |
Use this for small parametric props like vessels, columns, bulbs, or procedural geometry. Prefer authored assets when the silhouette needs hand-tuned asymmetry or broader asset-pipeline reuse. |
5.12 `aura.mesh.createFromVertices(vertices, indices, normals?, uvs?, colorsOrSkinning?, skinning?)`
aura.mesh.createFromVertices(
vertices: Float32Array | number[],
indices: Uint32Array | Uint16Array | number[],
normals?: Float32Array | number[] | null,
uvs?: Float32Array | number[] | null,
colorsOrSkinning?: Float32Array | number[] | {
jointIndices: Uint16Array | Uint32Array | number[];
jointWeights: Float32Array | number[];
} | null,
skinning?: {
jointIndices: Uint16Array | Uint32Array | number[];
jointWeights: Float32Array | number[];
} | null
): MeshHandle
| Property |
Value |
| Description |
Create a mesh from authored flat arrays. vertices and optional normals are xyz triples. Optional uvs are uv pairs. |
| Vertex colors |
If colorsOrSkinning is a numeric array, it is interpreted as flat per-vertex RGBA data with vertexCount * 4 entries. When omitted, vertex colors default to white. |
| Skinning |
Existing createFromVertices(..., skinning) calls remain valid. If the fifth argument is an object, Aura treats it as the legacy skinning payload. If colors are supplied, pass skinning as the sixth argument. |
| Validation |
vertices must contain whole xyz triples and indices must resolve to complete triangles. Optional normals, uvs, and vertex-color arrays must match the authored vertex count (*3, *2, and *4 respectively). Skinning payloads must provide aligned jointIndices and jointWeights quads per vertex. |
| Error behavior |
Throws TypeError for non-array vertex/index input and Error for malformed authored buffers, vertex-color payloads, or skinning data. |
| Usage note |
For voxel ambient occlusion and other baked per-vertex shading, prefer this vertex-color path over compensating scene-wide lighting hacks. Chunk meshes can use createFromVertices(..., colors) to preserve authored per-vertex lighting. |
5.13 `aura.mesh.getData(handle)`
aura.mesh.getData(handle: MeshHandle): {
vertexCount: number;
indexCount: number;
morphTargetCount: number;
bounds: { min: Vec3; max: Vec3 };
} | null
5.14 `aura.mesh.setMorphTargets(handle, targets)`
aura.mesh.setMorphTargets(handle: MeshHandle, targets: Array<{
positions: Float32Array | number[];
normals?: Float32Array | number[];
}>): void
| Property |
Value |
| Description |
Replace the mesh morph target set used by the draw3d morph shader path. Up to 4 targets are accepted per mesh. |
| Validation |
Invalid handle -> warning + no-op. targets must be an array of objects. Each positions buffer must contain vertexCount * 3 floats. Optional normals buffer must also contain vertexCount * 3 floats. More than 4 targets are truncated with warning. |
| Error behavior |
Throws TypeError for non-array input and Error for malformed target buffers. |
5.15 `aura.mesh.setMorphWeights(handle, weights)`
aura.mesh.setMorphWeights(handle: MeshHandle, weights: Float32Array | number[]): void
| Property |
Value |
| Description |
Update the per-draw morph blend weights. Missing entries default to 0. |
| Validation |
Invalid handle -> warning + no-op. weights must be a numeric array. Values past index 3 are ignored. |
| Error behavior |
Throws TypeError for non-array input. |
5.16 `aura.mesh.unload(handle)`
aura.mesh.unload(handle: MeshHandle): void
| Property |
Value |
| Validation |
Basic primitive creators (createBox, createSphere, createPlane, createCylinder, createCone, createTorus, createCapsule) keep their warning + fallback behavior for numeric range issues (1 unit defaults, segments>=3, rings>=2, torus defaults radial=24, tubular=16). getData with invalid handle returns null. unload with invalid handle is silent no-op. |
| Error behavior |
load(path) throws TypeError for non-string and Error for missing/unsupported mesh assets. Advanced authored generators (createRing, createExtrude, createLathe) throw on malformed authored input instead of guessing. |
| Supported formats |
glTF 2.0 binary (.glb) primary, OBJ (.obj) fallback. |
6) `aura.material`
6.1 `aura.material.create(options?)`
aura.material.create(options?: {
color?: Color;
texture?: string;
normalMap?: string;
metallic?: number;
roughness?: number;
alphaMode?: "opaque" | "mask" | "blend" | "hash";
alphaCutoff?: number;
doubleSided?: boolean;
sheenColor?: ColorRgb;
sheenRoughness?: number;
specularFactor?: number;
specularColor?: ColorRgb;
}): MaterialHandle
6.2 `aura.material.setColor(handle, color)`
aura.material.setColor(handle: MaterialHandle, color: Color): void
6.3 `aura.material.setTexture(handle, texturePath)`
aura.material.setTexture(handle: MaterialHandle, texturePath: string | DataTextureHandle | null): void
aura.material.setNormalMap(
handle: MaterialHandle,
input: string | DataTextureHandle | null
): void | { ok: false; reasonCode: string; reason: string }
6.3.2 `aura.material.setMetallicRoughnessTexture(handle, input)`
aura.material.setMetallicRoughnessTexture(
handle: MaterialHandle,
input: string | DataTextureHandle | null
): void | { ok: false; reasonCode: string; reason: string }
6.3.3 `aura.material.setOcclusionTexture(handle, input)`
aura.material.setOcclusionTexture(
handle: MaterialHandle,
input: string | DataTextureHandle | null
): void | { ok: false; reasonCode: string; reason: string }
6.3.4 `aura.material.setEmissiveTexture(handle, input)`
aura.material.setEmissiveTexture(
handle: MaterialHandle,
input: string | DataTextureHandle | null
): void | { ok: false; reasonCode: string; reason: string }
aura.material.setMetallic(handle: MaterialHandle, metallic: number): void
6.5 `aura.material.setRoughness(handle, roughness)`
aura.material.setRoughness(handle: MaterialHandle, roughness: number): void
aura.material.setMetallicRoughness(handle: MaterialHandle, metallic: number, roughness: number): void
6.7 `aura.material.setAlphaCutoff(handle, value)`
aura.material.setAlphaCutoff(handle: MaterialHandle, value: number): void | { ok: false; reasonCode: string; reason: string }
6.7.1 `aura.material.setAlphaMode(handle, mode)`
aura.material.setAlphaMode(
handle: MaterialHandle,
mode: "opaque" | "mask" | "blend" | "hash"
): void | { ok: false; reasonCode: string; reason: string }
6.8 `aura.material.setDoubleSided(handle, enabled)`
aura.material.setDoubleSided(handle: MaterialHandle, enabled: boolean): void | { ok: false; reasonCode: string; reason: string }
6.9 `aura.material.setSheen(handle, roughness, color)`
aura.material.setSheen(handle: MaterialHandle, roughness: number, color: ColorRgb): void | { ok: false; reasonCode: string; reason: string }
6.10 `aura.material.setSpecularFactor(handle, value)`
aura.material.setSpecularFactor(handle: MaterialHandle, value: number): void | { ok: false; reasonCode: string; reason: string }
6.10.1 `aura.material.setSpecularColor(handle, color)`
aura.material.setSpecularColor(
handle: MaterialHandle,
color: ColorRgb
): void | { ok: false; reasonCode: string; reason: string }
6.10.2 `aura.material.setOcclusionStrength(handle, value)`
aura.material.setOcclusionStrength(
handle: MaterialHandle,
value: number
): void | { ok: false; reasonCode: string; reason: string }
6.11 `aura.material.createCustom(options)`
aura.material.createCustom(options: {
vertex: string;
fragment: string;
uniforms?: Record<string, "float" | "vec2" | "vec3" | "vec4" | "mat4">;
texture?: boolean;
}): MaterialHandle
| Property |
Value |
| Description |
Create a custom WGSL-backed material that still uses Aura's fixed camera/model binding contract. |
| Required fields |
vertex and fragment must be non-empty WGSL strings. |
| Uniform contract |
uniforms is an object mapping names to "float", "vec2", "vec3", "vec4", or "mat4". Texture sampling is declared separately with texture: true; do not declare uniforms.texture = "texture". |
| Error behavior |
Throws TypeError for missing/invalid options, invalid shader-source strings, non-object uniforms, non-string uniform types, unknown uniform types, duplicate/empty uniform names, or non-boolean texture. |
| Runtime note |
Handle allocation happens before native shader compilation. If WGSL later fails native compilation, the handle is retired and later mutations resolve as missing_material_handle. |
| Guidance |
Use createCustom for narrow advanced materials or materials that genuinely need custom WGSL. Prefer the standard create(...) material path for normal lit scene content. |
aura.material.setUniform(
handle: MaterialHandle,
name: string,
value: number | number[] | { x: number; y?: number; z?: number; w?: number }
): void | { ok: false; reasonCode: string; reason: string }
| Property |
Value |
| Description |
Mutate one declared custom-shader uniform on an existing custom material. |
| Accepted values |
float accepts one finite number. vec2/vec3/vec4 accept flat arrays or {x,y[,z[,w]]} objects. mat4 accepts a flat 16-number array. |
| Failure reasons |
Structured failure objects use stable reason codes: invalid_material_handle, missing_material_handle, not_custom_shader_material, invalid_uniform_name, unknown_custom_uniform, invalid_uniform_value. |
| Guidance |
Keep these mutations narrow and declarative. If a material needs a large evolving authoring graph, it should probably not live on this seam. |
6.13 `aura.material.setCustomTexture(handle, texturePath)`
aura.material.setCustomTexture(
handle: MaterialHandle,
texturePath: string | null
): void | { ok: false; reasonCode: string; reason: string }
| Property |
Value |
| Description |
Bind or clear the single declared sampled 2D texture on a custom material. |
| Validation |
texture: true must have been declared at material creation time. null or '' clears the texture. |
| Failure reasons |
Structured failure objects use stable reason codes: invalid_material_handle, missing_material_handle, not_custom_shader_material, custom_texture_not_declared, invalid_custom_texture_path. |
| Runtime note |
String-path acceptance happens at the JS/binding seam. Actual file decode/bind occurs later on the native renderer path. |
6.14 `aura.material.reset(handle)`
aura.material.reset(handle: MaterialHandle): void
Resets material to deterministic defaults:
color = { r: 1, g: 1, b: 1, a: 1 }
metallic = 0
roughness = 1
texture = null (fallback/default texture)
6.15 `aura.material.clone(handle)`
aura.material.clone(handle: MaterialHandle): MaterialHandle
6.16 `aura.material.unload(handle)`
aura.material.unload(handle: MaterialHandle): void
| Property |
Value |
| Validation |
Invalid handle for standard mutators/unload -> structured failure on the newer advanced setters and silent no-op on the oldest frozen setters. Numeric ranges clamp where documented (metallic, roughness, alphaCutoff, sheenRoughness, specularFactor, and occlusionStrength to [0,1]; ior to [1,3]; thickness>=0). |
| Error behavior |
create throws TypeError only for non-object options. setTexture throws for non-string/non-number/non-null input and for unknown data-texture handles. setNormalMap, setMetallicRoughnessTexture, setOcclusionTexture, and setEmissiveTexture return structured failures on invalid handles or bad texture input using stable reason codes such as invalid_material_handle, missing_material_handle, invalid_texture_input, and unknown_data_texture_handle. setAlphaMode, setSpecularColor, and setOcclusionStrength also use structured failures such as invalid_alpha_mode, invalid_specular_color, and invalid_occlusion_strength_value. clone of invalid handle throws Error("Invalid material handle"). createCustom throws on malformed declarations, while bad WGSL compilation later retires the handle. |
| Runtime note |
setTexture live-binds albedo data textures. The secondary texture setters also accept data-texture handles, but the current native implementation snapshots the data texture into the material slot at bind time rather than live-linking later updateDataTexture(...) changes. |
Phase-2 supported controls:
- Runtime mutators:
setColor, setTexture, setNormalMap, setMetallicRoughnessTexture, setOcclusionTexture, setEmissiveTexture, setMetallic, setRoughness, setMetallicRoughness, setAlphaCutoff, setAlphaMode, setDoubleSided, setSheen, setSpecularFactor, setSpecularColor, setOcclusionStrength, setUniform, setCustomTexture, reset.
- Resource lifecycle:
create, clone, unload.
- Advanced material creation:
createCustom, plus create-time knobs such as alphaMode, alphaCutoff, doubleSided, sheenColor, sheenRoughness, specularFactor, and specularColor.
- PBR texture inputs accepted at create time:
texture, normalMap, metallicRoughnessTexture, aoTexture/occlusionTexture, emissiveTexture.
Explicit non-goals in this phase:
- No node-graph/shader-graph authoring surface.
- No per-material pipeline state overrides (blend/depth/cull/sampler mode).
- No full runtime parity for every create-time PBR knob yet (for example
clearcoat and the sheen/specular texture-backed lanes remain outside this runtime wave).
- No subsurface authoring surface.
Operator guidance:
- Prefer
createRing, createExtrude, and createLathe for small procedural shapes,
blockouts, and compact parametric props whose authored shape is cheaper to
express in code than to import as an asset.
- Prefer authored assets for scene-critical organic meshes, asymmetric props,
or content that artists need to iterate outside code.
- Prefer
createCustom for narrow, explicit WGSL needs where the standard PBR
material surface cannot express the effect.
- Prefer standard materials for most scene content so lighting,
textures, and tooling remain on the common path.
7) `aura.compute`
This section freezes the native compute surface exposed by the host runtime.
Browser capability declarations for compute are documented separately from this
native compute reference.
Type aliases (TypeScript notation):
type ComputePipelineHandle = number;
type ComputeBufferHandle = number;
type ComputeBindGroupHandle = number;
type ComputeBufferUsage =
| 'storage'
| 'uniform'
| 'storage-read'
| 'storage_read'
| 'readback'
| 'staging';
type ComputeBindGroupEntry = {
binding: number;
buffer: ComputeBufferHandle;
};
7.1 `aura.compute.createPipeline(wgslCode, entryPoint?)`
aura.compute.createPipeline(
wgslCode: string,
entryPoint?: string
): ComputePipelineHandle
| Property |
Value |
| Validation |
wgslCode must be a string. entryPoint defaults to "main" when omitted or non-string. |
| Error behavior |
Invalid wgslCode throws TypeError. WGSL compilation/runtime failures surface asynchronously through aura.compute.getError(handle). |
7.2 `aura.compute.createBuffer(size, usage?)`
aura.compute.createBuffer(
size: number,
usage?: ComputeBufferUsage
): ComputeBufferHandle
| Property |
Value |
| Validation |
size must be a finite number greater than 0. usage defaults to "storage" and accepts "storage", "uniform", "storage-read" / "storage_read", "readback" / "staging". |
| Error behavior |
Invalid size type throws TypeError. size <= 0 throws RangeError. Unknown usage throws TypeError. |
7.3 `aura.compute.writeBuffer(bufferHandle, data, offset?)`
aura.compute.writeBuffer(
bufferHandle: ComputeBufferHandle,
data: TypedArray | ArrayBuffer,
offset?: number
): void
| Property |
Value |
| Validation |
bufferHandle must be numeric. data must be a TypedArray or ArrayBuffer. offset defaults to 0. |
| Error behavior |
Invalid argument types throw TypeError. Invalid runtime handles report through aura.compute.getError(handle) after host processing. |
7.4 `aura.compute.createBindGroup(pipelineHandle, entries)`
aura.compute.createBindGroup(
pipelineHandle: ComputePipelineHandle,
entries: ComputeBindGroupEntry[]
): ComputeBindGroupHandle
| Property |
Value |
| Validation |
pipelineHandle must be numeric. entries must be an array of objects with numeric binding and buffer fields. |
| Error behavior |
Invalid argument types throw TypeError. Invalid runtime handles report through aura.compute.getError(handle) after host processing. |
7.5 `aura.compute.dispatch(pipelineHandle, bindGroupHandle, workgroupsX, workgroupsY?, workgroupsZ?)`
aura.compute.dispatch(
pipelineHandle: ComputePipelineHandle,
bindGroupHandle: ComputeBindGroupHandle,
workgroupsX: number,
workgroupsY?: number,
workgroupsZ?: number
): void
| Property |
Value |
| Validation |
pipelineHandle, bindGroupHandle, and workgroupsX must be numeric. workgroupsY/workgroupsZ default to 1. Workgroup counts are clamped to a minimum of 1. |
| Error behavior |
Invalid argument types throw TypeError. Runtime dispatch failures report through aura.compute.getError(handle). |
7.6 `aura.compute.readBuffer(bufferHandle, offset?, size?)`
aura.compute.readBuffer(
bufferHandle: ComputeBufferHandle,
offset?: number,
size?: number
): Float32Array | Uint8Array | null
| Property |
Value |
| Validation |
bufferHandle must be numeric. Returned data is Float32Array when byte length is 4-byte aligned; otherwise Uint8Array. |
| Error behavior |
Invalid argument types throw TypeError. The API is queued: passing a positive size enqueues readback and returns null; a later call returns the data once the host has processed it. |
7.7 `aura.compute.destroyPipeline(handle)`
aura.compute.destroyPipeline(handle: ComputePipelineHandle): void
7.8 `aura.compute.destroyBuffer(handle)`
aura.compute.destroyBuffer(handle: ComputeBufferHandle): void
7.9 `aura.compute.destroyBindGroup(handle)`
aura.compute.destroyBindGroup(handle: ComputeBindGroupHandle): void
7.10 `aura.compute.getError(handle)`
aura.compute.getError(
handle: ComputePipelineHandle | ComputeBufferHandle | ComputeBindGroupHandle
): string | null
| Property |
Value |
| Validation |
handle must be numeric. |
| Error behavior |
Invalid argument types throw TypeError. Returns the last queued runtime error for the handle, or null when no error is present. Reading the error consumes it. |
8) Error semantics summary (all 3D calls)
| Namespace |
Invalid Arguments |
Missing Asset/Resource |
Invalid Handle |
Runtime Failure |
aura.draw3d |
Warning + no-op/fallback |
drawSkybox throws Error |
N/A |
Warning; no crash |
aura.camera3d |
Warning + no-op/clamp |
N/A |
N/A |
Warning; keeps last valid camera state |
aura.light |
Constructors may throw TypeError for structurally invalid required args; otherwise warning |
N/A |
update/remove: silent no-op |
Warning; no crash |
aura.mesh |
Primitive creators warn + fallback defaults |
load throws Error |
getData -> null; unload no-op |
Warning; no crash |
aura.material |
Warning + clamp/no-op |
setTexture throws Error for missing path |
Mutators/unload no-op; clone throws on invalid handle |
Warning; no crash |
aura.compute |
Create/read/write/dispatch argument type mismatches throw TypeError; createBuffer(<=0) throws RangeError |
N/A |
Runtime handle failures surface via getError(handle) after queued host processing |
Warning/error string; no host crash |
Severity order mirrors v1: throw > warning + no-op/fallback > silent no-op.
9) Reference examples
9.1 `aura.draw3d`
| Call |
Input |
Expected |
drawMesh(1, 2) |
valid handles |
mesh draw enqueued |
drawMesh(-1, 2) |
invalid mesh handle |
warning, no-op |
drawMesh(1, 2, { scale: { x: 2, y: 2, z: 2 } }) |
valid transform |
scaled draw |
drawMesh(1, 2, { position: null }) |
null vec |
warning, fallback position |
drawSkybox("env/sky") |
existing asset |
skybox set |
drawSkybox("missing/sky") |
missing asset |
throws Error |
clear3d() |
no color |
default clear color |
clear3d({ r: 1, g: 0, b: 0 }) |
missing alpha |
alpha defaults to 1.0 |
9.2 `aura.camera3d`
| Call |
Input |
Expected |
perspective(60, 0.1, 1000) |
valid |
camera projection updated |
perspective(200, 0.1, 1000) |
fov out of range |
clamp to 179, warning |
perspective(60, -1, 1000) |
invalid near |
warning, no-op |
setPosition(0, 5, 10) |
valid |
camera moved |
setTarget(0, 0, 0) |
valid |
camera target updated |
lookAt(0, 0, 0) |
valid |
target set to point |
setFOV("wide") |
invalid type |
warning, no-op |
getViewMatrix() |
any time |
returns 16 numbers |
getProjectionMatrix() |
any time |
returns 16 numbers |
setControlProfile("orbit", { damping: 2 }) |
damping out of range |
clamp damping to 1, warning |
setControlProfile("freecam") |
unsupported profile |
warning, no-op |
updateControls(1/60, { rotateX: 2000 }) |
oversized delta |
clamp input to deterministic bounds |
updateControls(-1, {}) |
invalid dtSeconds |
warning, no-op |
getControlState() |
any time |
deterministic object snapshot with profile/active/orbit |
9.3 `aura.light`
| Call |
Input |
Expected |
ambient({r:1,g:1,b:1,a:1}, 0.3) |
valid |
returns light handle |
directional({x:1,y:-1,z:0}, {r:1,g:1,b:0.9,a:1}, 1) |
valid |
returns light handle |
point({x:0,y:2,z:0}, {r:1,g:0.8,b:0.5,a:1}, 2, 25) |
valid |
returns light handle |
spot({x:0,y:3,z:2}, {x:0,y:-1,z:0}, {r:1,g:1,b:1,a:1}, 2, 30, 0.7853982) |
valid |
returns light handle |
point({x:0,y:2,z:0}, {r:1,g:1,b:1,a:1}, 2, -1) |
invalid range |
warning + clamp/fallback |
update(10, { intensity: 0.5 }) |
valid handle |
light updated |
update(-1, { intensity: 0.5 }) |
invalid handle |
silent no-op |
remove(10) |
valid handle |
light removed |
remove(-1) |
invalid handle |
silent no-op |
9.4 `aura.mesh`
| Call |
Input |
Expected |
load("models/crate.glb") |
existing glb |
returns mesh handle |
load("models/crate.obj") |
existing obj |
returns mesh handle |
load("models/missing.glb") |
missing |
throws Error |
load(42 as any) |
non-string |
throws TypeError |
createBox() |
defaults |
returns mesh handle |
createSphere(1, 24) |
valid |
returns mesh handle |
createSphere(1, -2) |
invalid segments |
warning + fallback segments |
createPlane(10, 10) |
valid |
returns mesh handle |
createCylinder(1, 1, 16) |
valid |
returns mesh handle (vertexCount=68, indexCount=192) |
createCone(1, 1, 16) |
valid |
returns mesh handle (vertexCount=51, indexCount=96) |
createTorus(1, 0.3, 24, 16) |
valid |
returns mesh handle (vertexCount=425, indexCount=2304) |
createCapsule(0.5, 2, 16, 8) |
valid |
returns mesh handle (vertexCount=306, indexCount=1632) |
createFromVertices(vertices, indices) |
valid authored positions/indices |
returns mesh handle with default white vertex colors |
createFromVertices(vertices, indices, normals, uvs, colors) |
valid color array |
returns mesh handle with per-vertex RGBA colors |
createFromVertices(vertices, indices, normals, uvs, { jointIndices, jointWeights }) |
legacy 5th-arg skinning |
returns mesh handle using supplied skinning |
createFromVertices(vertices, indices, normals, uvs, colors, { jointIndices, jointWeights }) |
colors + supplied skinning |
returns mesh handle using both contracts together |
createFromVertices(vertices, indices, normals, uvs, [1, 0, 0]) |
malformed color array |
throws Error |
getData(handle) |
valid handle |
metadata object |
getData(-1) |
invalid handle |
null |
unload(handle) |
valid |
mesh released |
unload(-1) |
invalid handle |
silent no-op |
9.5 `aura.material`
| Call |
Input |
Expected |
create() |
defaults |
returns material handle |
create({ metallic: 0.8, roughness: 0.2 }) |
valid |
handle created |
create(null as any) |
invalid options type |
throws TypeError |
setColor(handle, {r:1,g:0,b:0,a:1}) |
valid |
color updated |
setMetallic(handle, 2) |
out of range |
clamped to 1, warning |
setRoughness(handle, -1) |
out of range |
clamped to 0, warning |
setTexture(handle, "textures/albedo.png") |
existing texture |
texture bound |
setTexture(handle, "textures/missing.png") |
missing texture |
throws Error |
setTexture(handle, null) |
clear texture |
texture removed |
clone(handle) |
valid handle |
returns new independent handle |
clone(-1) |
invalid handle |
throws Error("Invalid material handle") |
unload(handle) |
valid handle |
material released |
unload(-1) |
invalid handle |
silent no-op |
9.6 `aura.compute`
| Call |
Input |
Expected |
createPipeline(shader) |
valid WGSL string |
returns pipeline handle |
createPipeline(42 as any) |
invalid WGSL type |
throws TypeError |
createBuffer(4, "storage-read") |
valid |
returns buffer handle |
createBuffer(0) |
invalid size |
throws RangeError |
writeBuffer(buffer, new Float32Array([2])) |
valid |
buffer upload queued |
writeBuffer(buffer, {}) |
invalid data |
throws TypeError |
createBindGroup(pipeline, [{ binding: 0, buffer }]) |
valid |
returns bind group handle |
dispatch(pipeline, bindGroup, 1) |
valid |
compute dispatch queued |
readBuffer(buffer, 0, 4) |
first readback request |
returns null, readback queued |
readBuffer(buffer) |
later frame with completed readback |
returns Float32Array([3]) once readback completes |
getError(handle) |
no runtime fault |
null |
destroyBindGroup(handle) / destroyPipeline(handle) / destroyBuffer(handle) |
valid handle |
resource release queued |
9.7 Three.js-style reference example
| Vector |
Expected |
Scene graph hierarchy (scene3d.createNode/setParent/setLocalTransform/getWorldTransform/traverse/queryRaycast) |
Deterministic parent-child propagation, traversal order, and ray-hit ordering across repeated runs. |
Camera controls (camera3d.perspective/setPosition/setTarget/lookAt/setControlProfile/updateControls/getControlState) |
Deterministic, finite matrices and deterministic control-state snapshots across repeated runs. |
Interaction query (scene3d.queryRaycast) |
Deterministic nearest-hit selection and stable multi-hit ordering (distance/toi ASC, tie-break by nodeId ASC). |
Light lifecycle (light.ambient/directional/point/update/remove) |
Handles are allocated and mutable controls execute without runtime instability. |
Material lifecycle (material.create/clone/setColor/setMetallicRoughness/unload) |
Clone remains distinct from source and mutators are behaviorally active in the example loop. |
Draw submission (draw3d.clear3d/drawMesh) + mesh lifecycle (mesh.createBox/createCylinder/createCone/createTorus/createCapsule/getData/unload) |
Scene draw submissions and procedural mesh metadata remain deterministic across reruns. |
Non-goals for this example:
- Pixel-perfect frame matching with Three.js output.
- Post-processing and custom shader graphs beyond the documented API.
9.8 `aura.scene3d`
Query helper contract:
aura.scene3d.screenToRay(pixelX, pixelY)
aura.scene3d.pick(pixelX, pixelY, options?)
aura.scene3d.queryRaycast(originOrOptions, maybeDirection?, maybeOptions?)
aura.scene3d.raycast(origin, direction, options?)
| Method |
Contract |
screenToRay(pixelX, pixelY) |
Returns { ok: true, origin, direction } for valid window pixel coordinates, or { ok: false, reason } for invalid inputs or missing camera/viewport state. Stable failure reasons are invalid_screen_coords, missing_camera_or_window, invalid_viewport_size, missing_camera_matrices, singular_view_projection, degenerate_unproject, and degenerate_ray_direction. |
pick(pixelX, pixelY, options?) |
Convenience screen-space retained-scene query. Uses the current scene3d node graph rather than submitted draw meshes or physics bodies. Accepts maxDistance, firstOnly, and layerMask. Successful hits expose nodeId, distance, point, layer, meshHandle, materialHandle, visible, hasRenderBinding, normal: null, and triangleIndex: -1. |
queryRaycast(originOrOptions, maybeDirection?, maybeOptions?) |
Retained-scene query over authored scene3d nodes using node world transforms and scale-derived proxy bounds. Supports (origin, direction, options?) or { origin, direction, ...options }. Options are maxDistance, firstOnly, visibleOnly, requireRenderBinding, layerMask, includeNodeIds, and excludeNodeIds. Successful responses expose hit, hitCount, firstHit, hits, maxDistance, firstOnly, visibleOnly, requireRenderBinding, and layerMask. Each hit exposes nodeId, distance, toi (same numeric value as distance), point, radius, layer, meshHandle, materialHandle, visible, and hasRenderBinding. Stable validation failures return { ok: false, reason } with invalid_raycast_args, invalid_raycast_first_only, invalid_raycast_visible_only, invalid_raycast_require_render_binding, invalid_raycast_layer_mask, invalid_raycast_include_node_ids, or invalid_raycast_exclude_node_ids. |
raycast(origin, direction, options?) |
Draw-scene mesh query over submitted render data, not retained scene3d node identity. Accepts maxDistance, firstOnly, and testTriangles. Returns an array of mesh hits shaped like { meshHandle, distance, point, normal, triangleIndex }. Use this lane when you need draw-mesh or triangle-hit truth rather than authored node identity. |
Query split:
- Use
scene3d.queryRaycast(...) when gameplay needs authored nodeId identity, render-binding identity, or retained-scene filtering.
- Use
scene3d.pick(...) for screen-space picking on that same retained-scene lane.
- Use
scene3d.raycast(...) when gameplay needs draw-mesh hits or triangle tests against submitted render data.
- Use
physics3d.queryRaycast(...) when gameplay needs physics-body ownership or physics filtering.
10) Frozen surface summary
// aura.draw3d
drawMesh(mesh, material, transform?)
drawSkybox(path)
clear3d(color?)
billboard(textureHandleOrSource, options)
// aura.camera3d
perspective(fovDeg, near, far)
lookAt(x, y, z)
setPosition(x, y, z)
setTarget(x, y, z)
setFOV(fovDeg)
getViewMatrix()
getProjectionMatrix()
setControlProfile(profile, options?)
updateControls(dtSeconds, input?)
getControlState()
// aura.scene3d (helper)
createNode(initialTransform?)
removeNode(nodeId)
setParent(nodeId, parentId)
getParent(nodeId)
setLocalTransform(nodeId, transform)
getLocalTransform(nodeId)
getWorldTransform(nodeId)
traverse(rootOrCallback, maybeCallback)
screenToRay(pixelX, pixelY)
pick(pixelX, pixelY, options?)
queryRaycast(originOrOptions, maybeDirection?, maybeOptions?)
raycast(origin, direction, options?)
// aura.light
ambient(color, intensity)
hemisphere(skyColor, groundColor, intensity, upDirection?)
directional(direction, color, intensity)
point(position, color, intensity, range)
spot(position, direction, color, intensity, range, angleRadians)
update(handle, props)
remove(handle)
// aura.mesh
load(path)
createBox(width?, height?, depth?)
createSphere(radius?, segments?)
createPlane(width?, depth?)
createCylinder(radius?, height?, segments?)
createCone(radius?, height?, segments?)
createTorus(majorRadius?, minorRadius?, radialSegments?, tubularSegments?)
createCapsule(radius?, height?, segments?, rings?)
createFromVertices(vertices, indices, normals?, uvs?, colorsOrSkinning?, skinning?)
getData(handle)
unload(handle)
// aura.material
create(options?)
setColor(handle, color)
setTexture(handle, texturePath)
setMetallic(handle, metallic)
setRoughness(handle, roughness)
setMetallicRoughness(handle, metallic, roughness)
reset(handle)
clone(handle)
unload(handle)
// aura.compute
createPipeline(wgslCode, entryPoint?)
createBuffer(size, usage?)
writeBuffer(bufferHandle, data, offset?)
createBindGroup(pipelineHandle, entries)
dispatch(pipelineHandle, bindGroupHandle, workgroupsX, workgroupsY?, workgroupsZ?)
readBuffer(bufferHandle, offset?, size?)
destroyPipeline(handle)
destroyBuffer(handle)
destroyBindGroup(handle)
getError(handle)
Any signature or error-behavior change above is a breaking API change and requires a major contract version bump.