AURA

JSGG

AuraJS
DOCSEXAMPLESGITHUB
API Contract 3D
Canonical 3D API signatures, validation rules, and renderer-facing semantics.
docs/api-contract-3d.md

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

  1. Animation Mixer and Crossfade
  2. Physics3D Joint Integration
  3. Terrain Runtime Integration
  4. Conventions
  5. aura.draw3d
  6. aura.camera3d
  7. aura.light
  8. aura.mesh
  9. aura.material
  10. aura.compute
  11. Error Semantics Summary
  12. Reference Examples
  13. 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:


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.

2.1 `aura.draw3d.drawMesh(mesh, material, transform?)`

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.

2.2 `aura.draw3d.drawMeshInstanced(mesh, material, transforms)`

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.

3.9 `aura.camera3d.updateControls(dtSeconds, input?)`

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

4.10 `aura.light.configureDirectionalShadows(options)`

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

4.11 `aura.light.configureShadow(lightId, options)`

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

6.3.1 `aura.material.setNormalMap(handle, input)`

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 }

6.4 `aura.material.setMetallic(handle, metallic)`

aura.material.setMetallic(handle: MaterialHandle, metallic: number): void

6.5 `aura.material.setRoughness(handle, roughness)`

aura.material.setRoughness(handle: MaterialHandle, roughness: number): void

6.6 `aura.material.setMetallicRoughness(handle, metallic, roughness)`

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.

6.12 `aura.material.setUniform(handle, name, value)`

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.

DOCUMENT REFERENCE
docs/api-contract-3d.md
AURAJS
Cmd/Ctrl+K
aurajsgg