@aura3d/cli 5.1.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,8 @@
1
+ export declare const PHYSICS_BACKEND_INSTALL_COMMAND = "npm install @dimforge/rapier3d-compat@0.20.0";
2
+ /**
3
+ * Lint-time signal for the typed PHYSICS_BACKEND_MISSING runtime error: a project whose sources call `.physics(...)`,
4
+ * `app.physics` or `PhysicsWorld` must be able to resolve the Rapier backend. Returns one failure line (with the exact
5
+ * install command) or an empty list.
6
+ */
7
+ export declare function physicsBackendFailures(projectDir: string): string[];
8
+ //# sourceMappingURL=physics-backend-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"physics-backend-check.d.ts","sourceRoot":"","sources":["../src/physics-backend-check.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,+BAA+B,iDAAkC,CAAC;AAe/E;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAcnE"}
@@ -0,0 +1,44 @@
1
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { join, resolve } from "node:path";
4
+ const BACKEND = "@dimforge/rapier3d-compat";
5
+ export const PHYSICS_BACKEND_INSTALL_COMMAND = `npm install ${BACKEND}@0.20.0`;
6
+ const PHYSICS_USE = /\.physics\s*\(|\bapp\.physics\b|\bpreloadPhysics\b|\bPhysicsWorld\b|\bphysics\s*:\s*\{/u;
7
+ function sourceFiles(directory, depth = 0, found = []) {
8
+ if (depth > 6 || found.length > 400 || !existsSync(directory))
9
+ return found;
10
+ for (const name of readdirSync(directory)) {
11
+ if (name === "node_modules" || name === "dist" || name.startsWith("."))
12
+ continue;
13
+ const path = join(directory, name);
14
+ const stats = statSync(path);
15
+ if (stats.isDirectory())
16
+ sourceFiles(path, depth + 1, found);
17
+ else if (/\.(?:ts|tsx|mts|js|jsx|mjs)$/u.test(name) && !name.endsWith(".d.ts") && stats.size < 512 * 1024)
18
+ found.push(path);
19
+ }
20
+ return found;
21
+ }
22
+ /**
23
+ * Lint-time signal for the typed PHYSICS_BACKEND_MISSING runtime error: a project whose sources call `.physics(...)`,
24
+ * `app.physics` or `PhysicsWorld` must be able to resolve the Rapier backend. Returns one failure line (with the exact
25
+ * install command) or an empty list.
26
+ */
27
+ export function physicsBackendFailures(projectDir) {
28
+ const root = resolve(projectDir);
29
+ const used = sourceFiles(join(root, "src")).filter((file) => PHYSICS_USE.test(readFileSync(file, "utf8")));
30
+ if (used.length === 0)
31
+ return [];
32
+ let resolvable = false;
33
+ try {
34
+ createRequire(join(root, "package.json")).resolve(`${BACKEND}/package.json`);
35
+ resolvable = true;
36
+ }
37
+ catch { /* not installed */ }
38
+ if (!resolvable) {
39
+ // Some package exports maps hide package.json; fall back to the installed directory.
40
+ resolvable = existsSync(join(root, "node_modules", BACKEND, "package.json"));
41
+ }
42
+ return resolvable ? [] : [`PHYSICS_BACKEND_MISSING: ${used.length} source file(s) use Aura3D physics (first: ${used[0].slice(root.length + 1)}) but ${BACKEND} is not installed. Run: ${PHYSICS_BACKEND_INSTALL_COMMAND}`];
43
+ }
44
+ //# sourceMappingURL=physics-backend-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"physics-backend-check.js","sourceRoot":"","sources":["../src/physics-backend-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,OAAO,GAAG,2BAA2B,CAAC;AAC5C,MAAM,CAAC,MAAM,+BAA+B,GAAG,eAAe,OAAO,SAAS,CAAC;AAC/E,MAAM,WAAW,GAAG,yFAAyF,CAAC;AAE9G,SAAS,WAAW,CAAC,SAAiB,EAAE,KAAK,GAAG,CAAC,EAAE,QAAkB,EAAE;IACrE,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5E,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC;QAC1C,IAAI,IAAI,KAAK,cAAc,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QACjF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACnC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,KAAK,CAAC,WAAW,EAAE;YAAE,WAAW,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;aACxD,IAAI,+BAA+B,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,IAAI,GAAG,GAAG,GAAG,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9H,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,UAAkB;IACvD,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAC3G,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,IAAI,CAAC;QACH,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,OAAO,eAAe,CAAC,CAAC;QAC7E,UAAU,GAAG,IAAI,CAAC;IACpB,CAAC;IAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,CAAC;IAC/B,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,qFAAqF;QACrF,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,4BAA4B,IAAI,CAAC,MAAM,8CAA8C,IAAI,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,SAAS,OAAO,2BAA2B,+BAA+B,EAAE,CAAC,CAAC;AAC9N,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aura3d/cli",
3
- "version": "5.1.0",
3
+ "version": "5.2.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "aura3d": "dist/cli.js",
@@ -27,11 +27,11 @@
27
27
  "main": "./dist/index.js",
28
28
  "types": "./dist/index.d.ts",
29
29
  "dependencies": {
30
- "@aura3d/asset-index": "5.1.0",
31
- "@aura3d/assets": "5.1.0",
32
- "@aura3d/audio": "5.1.0",
33
- "@aura3d/rendering": "5.1.0",
34
- "create-aura3d": "5.1.0",
30
+ "@aura3d/asset-index": "5.2.0",
31
+ "@aura3d/assets": "5.2.0",
32
+ "@aura3d/audio": "5.2.0",
33
+ "@aura3d/rendering": "5.2.0",
34
+ "create-aura3d": "5.2.0",
35
35
  "typescript": "^5.8.3"
36
36
  },
37
37
  "description": "Bring real GLB/glTF assets into Aura3D browser apps with typed references, provenance, catalog search, validation, screenshots, and deploy checks.",
@@ -24,7 +24,7 @@ const app = createAuraApp("#app", { scene: scene({ background: "#101418" }) });
24
24
 
25
25
  | Call | Returns | Options |
26
26
  |---|---|---|
27
- | `createAuraApp(target: AuraAppTarget, options: AuraCreateAppOptions)` | `AuraApp` | options: frameMode?: "sync" \| "async" \| undefi…; scene: AuraSceneBuilder \| AuraSc…; worker?: { readonly createWorker: …; physics?: { readonly layers?: AuraC…; diagnostics?: boolean \| AuraDiagnostics…; renderer?: AuraCreateAppRendererOpti…; performanceQuality?: AuraPerformanceQuality \| …; quality?: AuraQualityMode \| undefin…; preserveDrawingBuffer?: boolean \| undefined; autoStart?: boolean \| undefined; resize?: boolean \| undefined; pixelRatio?: number \| { readonly max?:…; qualityRebuild?: { readonly flags?: import…; lightin… |
27
+ | `createAuraApp(target: AuraAppTarget, options: AuraCreateAppOptions)` | `AuraApp` | options: frameMode?: "sync" \| "async" \| undefi…; scene: AuraSceneBuilder \| AuraSc…; worker?: { readonly createWorker: …; physics?: { readonly layers?: AuraC…; diagnostics?: boolean \| AuraDiagnostics…; renderer?: AuraCreateAppRendererOpti…; performanceQuality?: AuraPerformanceQuality \| …; quality?: AuraQualityMode \| undefin…; preserveDrawingBuffer?: boolean \| undefined; autoStart?: boolean \| undefined; resize?: boolean \| undefined; routeState?: boolean \| undefined; pixelRatio?: number \| { readonly max?:…; qualityRebuild?: {… |
28
28
  | `app.onFrame(callback: AuraFrameCallback)` | `() => void` | |
29
29
  | `app.ready()` | `Promise<void>` | |
30
30
  | `app.nodes` | `AuraRuntimeNodeRegistry` | |
@@ -78,7 +78,7 @@ void camera.frame(primitives.box({ name: "subject" }), { fill: 0.6 });
78
78
 
79
79
  ## `looks`
80
80
 
81
- Genre look presets: environment, key light, fog and grade in one unit. Spread `looks.appOptions(id)` into `createAuraApp` options. HDRI-backed presets load `/hdri/<file>.hdr` from `public/hdri/`; a missing file degrades to a procedural probe, logs one `LOOK_HDRI_MISSING` console error and lists it in `app.diagnostics().warnings`.
81
+ Genre look presets: environment, key light, fog and grade in one unit. Spread `looks.appOptions(id)` into `createAuraApp` options. HDRI-backed presets load `/hdri/<file>.hdr` from `public/hdri/`; a missing file degrades to a procedural probe, logs one `LOOK_HDRI_MISSING` console error and lists it in `app.diagnostics().warnings`. `looks.heroStage({ target, radius, background })` is studio IBL + key/fill/rim + a floor that fades into `background`; `looks.heroRig` is the lights alone; `looks.fogForExtent({ extent })` sizes fog by distance. A scene with NO look, light, environment or effect gets the hero stage automatically at mount (any authored look/light/environment/effect, or a scene kit, turns it off; `?a3d-cold-start-look=0` forces it off).
82
82
 
83
83
  ```ts
84
84
  void looks.preset("product-studio");
@@ -86,7 +86,11 @@ void looks.preset("product-studio");
86
86
 
87
87
  | Call | Returns | Options |
88
88
  |---|---|---|
89
- | `looks.preset(id: AuraLookId, overrides: AuraLookOverrides \| undefined)` (+1 overloads) | `AuraNodeBuilder<AuraGroupNode>` | overrides: sun?: { readonly azimuthDeg?: n…; exposureEv?: number \| undefined; fogDensityScale?: number \| undefined; accent?: string \| undefined; background?: string \| undefined; hdri?: AuraHdriId \| AuraAssetRef…; environmentIntensity?: number \| undefined; environmentRotationDeg?: number \| undefined; key?: { readonly color?: AuraCo…; rim?: false \| { readonly color?…; fog?: { readonly color?: AuraCo…; grade?: { readonly contrast?: num…; post?: AuraPostPresetId \| { read… |
89
+ | `looks.heroRig(options?: AuraHeroRigOptions)` | `AuraNodeBuilder<AuraGroupNode>` | options: target?: AuraVec3 \| undefined; radius?: number \| undefined; intensity?: number \| undefined; keyColor?: string \| undefined; fillColor?: string \| undefined; rimColor?: string \| undefined; shadow?: boolean \| undefined; name?: string \| undefined |
90
+ | `looks.heroStage(options?: AuraHeroStageOptions)` | `AuraNodeBuilder<AuraGroupNode>` | options: environment?: "none" \| "studio" \| "neut…; environmentIntensity?: number \| undefined; background?: string \| undefined; floor?: false \| { readonly color?…; target?: AuraVec3 \| undefined; radius?: number \| undefined; intensity?: number \| undefined; keyColor?: string \| undefined; fillColor?: string \| undefined; rimColor?: string \| undefined; shadow?: boolean \| undefined; name?: string \| undefined |
91
+ | `looks.fogForExtent(options: AuraFogForExtentOptions)` | `AuraNodeBuilder<AuraEffectNode>` | options: extent: number; color?: string \| undefined; haze?: number \| undefined; mode?: "exp" \| "exp2" \| undefined; name?: string \| undefined |
92
+ | `looks.fogDensityForExtent(extent: number, haze?: number, mode?: "exp" \| "exp2")` | `number` | |
93
+ | `looks.preset(id: AuraLookId, overrides: AuraLookOverrides \| undefined)` (+1 overloads) | `AuraNodeBuilder<AuraGroupNode>` | overrides: sun?: { readonly azimuthDeg?: n…; exposureEv?: number \| undefined; fogDensityScale?: number \| undefined; accent?: string \| undefined; background?: string \| undefined; hdri?: AuraAssetRef<"texture"> \|…; environmentIntensity?: number \| undefined; environmentRotationDeg?: number \| undefined; key?: { readonly color?: AuraCo…; rim?: false \| { readonly color?…; fog?: { readonly color?: AuraCo…; grade?: { readonly contrast?: num…; post?: AuraPostPresetId \| { read… |
90
94
  | `looks.nodes(id: AuraLookId, overrides: AuraLookOverrides \| undefined)` | `readonly AuraNodeBuilder<AuraSceneNode>[]` | same as `looks.preset` |
91
95
  | `looks.appOptions(id: AuraLookId, options: AuraLookBuildOptions \| undefined)` | `Pick<AuraCreateAppOptions, "pixelRatio" \| "renderer">` | options: expansion?: "auto" \| "v0" \| "v1" \| un…; flags?: QrFlags \| undefined |
92
96
  | `looks.list()` | `readonly AuraLookId[]` | |
@@ -103,7 +107,7 @@ void material.pbr({ color: "#c8ccd4", metallic: 0.9, roughness: 0.38 });
103
107
 
104
108
  | Call | Returns | Options |
105
109
  |---|---|---|
106
- | `material.custom(spec: AuraCustomMaterialSpec)` | `AuraMaterialSpec` | spec: glsl: { readonly vertex?: strin…; wgsl?: { readonly vertex?: strin…; uniforms?: Readonly<Record<string, A…; uniformAliases?: Readonly<Record<string, s…; defines?: Readonly<Record<string, s…; transparent?: boolean \| undefined; depthWrite?: boolean \| undefined; depthTest?: boolean \| undefined; side?: "front" \| "back" \| "doubl…; blending?: "custom" \| "normal" \| "ad…; fog?: boolean \| undefined; patch?: Readonly<Record<string, s…; receiveShadow?: boolean \| undefined; castShadow?: boolean \| undefined |
110
+ | `material.custom(spec: AuraCustomMaterialSpec)` | `AuraMaterialSpec` | spec: glsl: { readonly vertex?: strin…; wgsl?: { readonly vertex?: strin…; uniforms?: Readonly<Record<string, A…; uniformAliases?: Readonly<Record<string, s…; defines?: Readonly<Record<string, s…; transparent?: boolean \| undefined; depthWrite?: boolean \| undefined; depthTest?: boolean \| undefined; side?: "front" \| "back" \| "doubl…; blending?: "additive" \| "multiply" \|…; fog?: boolean \| undefined; patch?: Readonly<Record<string, s…; receiveShadow?: boolean \| undefined; castShadow?: boolean \| undefined |
107
111
  | `material.toon(options?: AuraToonMaterialOptions)` | `AuraMaterialSpec` | options: color?: string \| undefined; steps?: number \| undefined; ramp?: AuraAssetRef<"texture", s…; shadowColor?: string \| undefined; rim?: AuraMaterialRimSpec \| und… |
108
112
  | `material.strata(options?: AuraStrataMaterialOptions)` | `AuraMaterialSpec` | options: bands?: number \| undefined; colors?: readonly string[] \| undef…; heightRange?: readonly [number, number]…; noise?: number \| undefined; noiseScale?: number \| undefined; seam?: number \| undefined; variation?: number \| undefined; slope?: AuraStrataSlopeOptions \| …; roughness?: number \| undefined; seed?: number \| undefined; side?: "front" \| "back" \| "doubl… |
109
113
  | `material.caustics(options?: AuraCausticsMaterialOptions)` | `AuraMaterialSpec` | options: color?: string \| undefined; causticColor?: string \| undefined; strength?: number \| undefined; scale?: number \| undefined; speed?: number \| undefined; surfaceY?: number \| undefined; depthFade?: number \| undefined; roughness?: number \| undefined; side?: "front" \| "back" \| "doubl… |
@@ -114,10 +118,10 @@ void material.pbr({ color: "#c8ccd4", metallic: 0.9, roughness: 0.38 });
114
118
  | `material.distance(options?: AuraDistanceMaterialOptions)` | `AuraMaterialSpec` | options: referencePosition?: AuraVec3 \| undefined; near?: number \| undefined; far?: number \| undefined; side?: "front" \| "back" \| "doubl… |
115
119
  | `material.shadow(options?: AuraShadowMaterialOptions)` | `AuraMaterialSpec` | options: color?: string \| undefined; opacity?: number \| undefined; side?: "front" \| "back" \| "doubl…; light?: "directional" \| "point" \|… |
116
120
  | `material.node(options: TslMaterialNodeOptions \| ShaderGraphMaterialOptions)` | `AuraMaterialSpec` | |
117
- | `material.pbr(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
121
+ | `material.pbr(rawOptions?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
118
122
  | `material.physical(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
119
123
  | `material.emissive(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
120
- | `material.metal(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
124
+ | `material.metal(rawOptions?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
121
125
  | `material.rubber(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
122
126
  | `material.glass(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
123
127
  | `material.clearcoat(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
@@ -130,6 +134,8 @@ void material.pbr({ color: "#c8ccd4", metallic: 0.9, roughness: 0.38 });
130
134
  | `material.brushedMetal(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
131
135
  | `material.frostedGlass(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
132
136
  | `material.clearGlass(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
137
+ | `material.studioMetal(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
138
+ | `material.studioGlass(options?: AuraMaterialSpec & { readonly tint?: AuraColor; })` | `AuraMaterialSpec` | |
133
139
  | `material.blackRubber(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
134
140
  | `material.matteClay(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
135
141
  | `material.ceramic(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
@@ -142,7 +148,7 @@ void material.pbr({ color: "#c8ccd4", metallic: 0.9, roughness: 0.38 });
142
148
  | `material.carPaint(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
143
149
  | `material.planet(preset?: AuraSolarPlanetMaterialPreset, options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
144
150
  | `material.atmosphere(options?: AuraMaterialSpec)` | `AuraMaterialSpec` | |
145
- | `material.proceduralTexture(texture: AuraProceduralTextureKind, options?: Partial<Omit<AuraProceduralTextureSpec, "texture" \| "kind">>)` | `AuraProceduralTextureSpec` | options: scale?: number \| undefined; strength?: number \| undefined; contrast?: number \| undefined; direction?: AuraVec3 \| undefined; colorA?: string \| undefined; colorB?: string \| undefined |
151
+ | `material.proceduralTexture(texture: AuraProceduralTextureKind, options?: Partial<Omit<AuraProceduralTextureSpec, "texture" \| "kind">>)` | `AuraProceduralTextureSpec` | options: strength?: number \| undefined; contrast?: number \| undefined; scale?: number \| undefined; direction?: AuraVec3 \| undefined; colorA?: string \| undefined; colorB?: string \| undefined |
146
152
  | `material.proceduralTextures` | `value` | |
147
153
  | `material.parameters(name: string, spec: AuraMaterialSpec)` | `AuraEditableMaterialParameters` | |
148
154
  | `material.fromParameters(parameters: AuraEditableMaterialParameters)` | `AuraMaterialSpec` | parameters: kind: "aura-material-parameters"; name: string; material: AuraMaterialSpec; roughness: number; metallic: number; metalness: number; transmission: number; clearcoat: number; clearcoatRoughness: number; thickness: number; ior: number; sheen: number; iridescence: number; anisotropy: number; envMapIntensity: number; emissiveIntensity: number |
@@ -164,11 +170,11 @@ void lights.directional({ position: [4, 6, 3], target: [0, 0, 0], intensity: 3,
164
170
  |---|---|---|
165
171
  | `lights.lensFlare(options?: AuraLensFlareOptions)` | `AuraNodeBuilder<AuraSceneNode>` | options: name?: string \| undefined; position?: AuraVec3 \| undefined; light?: string \| undefined; elements?: readonly AuraLensFlareEle…; occlusion?: AuraLensFlareOcclusion \| …; edgeFade?: number \| undefined; intensity?: number \| undefined; runtime?: AuraRuntimeNodeSpec \| und… |
166
172
  | `lights.ambient(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; intensity?: number \| undefined; color?: string \| undefined |
167
- | `lights.hemisphere(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; skyColor?: string \| undefined; groundColor?: string \| undefined; intensity?: number \| undefined; position?: AuraVec3 \| undefined |
173
+ | `lights.hemisphere(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; skyColor?: string \| undefined; groundColor?: string \| undefined; intensity?: number \| undefined; position?: AuraVec3 \| undefined; color?: string \| undefined |
168
174
  | `lights.lightProbe(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; sh?: readonly number[] \| undef…; skyColor?: string \| undefined; groundColor?: string \| undefined; intensity?: number \| undefined; position?: AuraVec3 \| undefined |
169
- | `lights.directional(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; position?: AuraVec3 \| undefined; target?: AuraVec3 \| undefined; intensity?: number \| undefined; color?: string \| undefined; shadow?: boolean \| AuraDirectional… |
170
- | `lights.point(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; position?: AuraVec3 \| undefined; target?: AuraVec3 \| undefined; intensity?: number \| undefined; power?: number \| undefined; distance?: number \| undefined; decay?: number \| undefined; color?: string \| undefined; shadow?: boolean \| AuraLocalShadow…; photometricProfile?: AuraPhotometricProfile \| … |
171
- | `lights.spot(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; position?: AuraVec3 \| undefined; target?: AuraVec3 \| undefined; angle?: number \| undefined; penumbra?: number \| undefined; distance?: number \| undefined; decay?: number \| undefined; intensity?: number \| undefined; power?: number \| undefined; color?: string \| undefined; shadow?: boolean \| AuraLocalShadow…; photometricProfile?: AuraPhotometricProfile \| …; projectedImage?: AuraProjectedLightImage \|… |
175
+ | `lights.directional(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; position?: AuraVec3 \| undefined; target?: AuraVec3 \| undefined; intensity?: number \| undefined; color?: string \| undefined; shadow?: boolean \| AuraDirectional…; castShadow?: boolean \| undefined |
176
+ | `lights.point(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; position?: AuraVec3 \| undefined; target?: AuraVec3 \| undefined; intensity?: number \| undefined; power?: number \| undefined; distance?: number \| undefined; decay?: number \| undefined; color?: string \| undefined; shadow?: boolean \| AuraLocalShadow…; castShadow?: boolean \| undefined; photometricProfile?: AuraPhotometricProfile \| … |
177
+ | `lights.spot(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; position?: AuraVec3 \| undefined; target?: AuraVec3 \| undefined; angle?: number \| undefined; penumbra?: number \| undefined; distance?: number \| undefined; decay?: number \| undefined; intensity?: number \| undefined; power?: number \| undefined; color?: string \| undefined; shadow?: boolean \| AuraLocalShadow…; castShadow?: boolean \| undefined; photometricProfile?: AuraPhotometricProfile \| …; projectedImage?: AuraProjectedLightImage \|… |
172
178
  | `lights.studio(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: intensity?: number \| undefined |
173
179
  | `lights.rect(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | options: name?: string \| undefined; position?: AuraVec3 \| undefined; target?: AuraVec3 \| undefined; intensity?: number \| undefined; color?: string \| undefined; width?: number \| undefined; height?: number \| undefined; twoSided?: boolean \| undefined |
174
180
  | `lights.rectArea(options?: {…})` | `AuraNodeBuilder<AuraLightNode>` | same as `lights.rect` |
@@ -186,23 +192,23 @@ void environments.neutral({ intensity: 0.8, background: false });
186
192
 
187
193
  | Call | Returns | Options |
188
194
  |---|---|---|
189
- | `environments.outdoor(options?: {…})` | `WorldEnvBuilderOut` | options: texture?: AuraAssetRef<"texture", s…; name?: string \| undefined; intensity?: number \| undefined; color?: string \| undefined; reflectionTexture?: AuraAssetRef<"texture", s…; rotation?: number \| undefined; biome?: OutdoorBiome \| undefined |
190
- | `environments.room(options?: {…})` | `WorldEnvBuilderOut` | options: texture?: AuraAssetRef<"texture", s…; name?: string \| undefined; intensity?: number \| undefined; color?: string \| undefined; reflectionTexture?: AuraAssetRef<"texture", s…; rotation?: number \| undefined; colorTemperatureK?: number \| undefined |
191
- | `environments.space(options?: Partial<Omit<AuraEnvironmentNode, "environment" \| "kind">>)` | `WorldEnvBuilderOut` | options: texture?: AuraAssetRef<"texture", s…; name?: string \| undefined; intensity?: number \| undefined; color?: string \| undefined; reflectionTexture?: AuraAssetRef<"texture", s…; rotation?: number \| undefined |
195
+ | `environments.outdoor(options?: {…})` | `WorldEnvBuilderOut` | options: texture?: AuraAssetRef<"texture", s…; name?: string \| undefined; intensity?: number \| undefined; color?: string \| undefined; rotation?: number \| undefined; reflectionTexture?: AuraAssetRef<"texture", s…; biome?: OutdoorBiome \| undefined |
196
+ | `environments.room(options?: {…})` | `WorldEnvBuilderOut` | options: texture?: AuraAssetRef<"texture", s…; name?: string \| undefined; intensity?: number \| undefined; color?: string \| undefined; rotation?: number \| undefined; reflectionTexture?: AuraAssetRef<"texture", s…; colorTemperatureK?: number \| undefined |
197
+ | `environments.space(options?: Partial<Omit<AuraEnvironmentNode, "environment" \| "kind">>)` | `WorldEnvBuilderOut` | options: texture?: AuraAssetRef<"texture", s…; name?: string \| undefined; intensity?: number \| undefined; color?: string \| undefined; rotation?: number \| undefined; reflectionTexture?: AuraAssetRef<"texture", s… |
192
198
  | `environments.underwater(options?: Partial<Omit<AuraEnvironmentNode, "environment" \| "kind">>)` | `WorldEnvBuilderOut` | same as `environments.space` |
193
199
  | `environments.preset(name: AuraEnvironmentPresetName, options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | options: name?: string \| undefined; intensity?: number \| undefined; diffuseIntensity?: number \| undefined; specularIntensity?: number \| undefined; rotation?: number \| undefined; background?: boolean \| AuraEnvironment…; color?: string \| undefined |
194
200
  | `environments.neutral(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
195
201
  | `environments.none(options?: {…})` | `AuraNodeBuilder<AuraEnvironmentNode>` | options: name?: string \| undefined |
196
- | `environments.capture(options?: {…})` | `AuraNodeBuilder<AuraEnvironmentNode>` | options: name?: string \| undefined; intensity?: number \| undefined; diffuseIntensity?: number \| undefined; specularIntensity?: number \| undefined; rotation?: number \| undefined; background?: boolean \| AuraEnvironment…; color?: string \| undefined; position?: AuraVec3 \| undefined; resolution?: 64 \| 128 \| 256 \| undefined; update?: "once" \| "on-demand" \| { …; include?: "all" \| "sky-only" \| unde… |
202
+ | `environments.capture(options?: {…})` | `AuraNodeBuilder<AuraEnvironmentNode>` | options: name?: string \| undefined; intensity?: number \| undefined; diffuseIntensity?: number \| undefined; specularIntensity?: number \| undefined; rotation?: number \| undefined; background?: boolean \| AuraEnvironment…; color?: string \| undefined; position?: AuraVec3 \| undefined; resolution?: 64 \| 128 \| 256 \| undefined; update?: "on-demand" \| "once" \| { …; include?: "all" \| "sky-only" \| unde… |
197
203
  | `environments.hdri(options: {…})` | `AuraNodeBuilder<AuraEnvironmentNode>` | options: name?: string \| undefined; intensity?: number \| undefined; diffuseIntensity?: number \| undefined; specularIntensity?: number \| undefined; rotation?: number \| undefined; background?: boolean \| AuraEnvironment…; color?: string \| undefined; texture: AuraAssetRef<"texture">; reflectionTexture?: AuraAssetRef<"texture"> \|… |
198
- | `environments.studio(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
204
+ | `environments.studio(options?: AuraStudioEnvironmentOptions)` | `AuraNodeBuilder<AuraEnvironmentNode>` | options: preset?: "studio" \| "softbox" \| un…; name?: string \| undefined; intensity?: number \| undefined; diffuseIntensity?: number \| undefined; specularIntensity?: number \| undefined; rotation?: number \| undefined; background?: boolean \| AuraEnvironment…; color?: string \| undefined |
199
205
  | `environments.materialLab(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
200
206
  | `environments.productHero(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
201
207
  | `environments.nightCinematic(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
202
208
  | `environments.metalStudio(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
203
209
  | `environments.glassStudio(options?: AuraEnvironmentOptionsV2)` | `AuraNodeBuilder<AuraEnvironmentNode>` | same as `environments.preset` |
204
210
  | `environments.presets()` | `readonly AuraEnvironmentMapPreset[]` | |
205
- | `environments.forMaterial(materialClass: "product" \| "metal" \| "glass" \| "studio")` | `AuraNodeBuilder<AuraEnvironmentNode>` | |
211
+ | `environments.forMaterial(materialClass: "product" \| "studio" \| "metal" \| "glass")` | `AuraNodeBuilder<AuraEnvironmentNode>` | |
206
212
 
207
213
  ## `sky`
208
214
 
@@ -214,8 +220,8 @@ void sky.preetham({ sun: { elevationDeg: 35, azimuthDeg: 120 } });
214
220
 
215
221
  | Call | Returns | Options |
216
222
  |---|---|---|
217
- | `sky.preetham(options: AuraSkyPreethamOptions)` | `AuraNodeBuilder<AuraSceneNode>` | options: moon?: unknown; groundColor?: string \| undefined; sun: AuraSkySunSpec; turbidity?: number \| undefined; rayleigh?: number \| undefined; mieCoefficient?: number \| undefined; mieDirectionalG?: number \| undefined; exposure?: number \| undefined; stars?: AuraSkyStarsOptions \| und…; clouds?: AuraSkyCloudsOptions \| un…; name?: string \| undefined; captureEnvironment?: boolean \| undefined; affectsFog?: boolean \| undefined |
218
- | `sky.gradient(options: AuraSkyGradientOptions)` | `AuraNodeBuilder<AuraSceneNode>` | options: moon?: unknown; intensity?: number \| undefined; clouds?: AuraSkyCloudsOptions \| un…; sun?: AuraSkySunSpec \| undefined; zenith: string; horizon: string; ground?: string \| undefined; exponent?: number \| undefined; horizonGlow?: number \| undefined; bands?: readonly AuraSkyBand[] \| …; stars?: AuraSkyStarsOptions \| und…; name?: string \| undefined; captureEnvironment?: boolean \| undefined; affectsFog?: boolean \| undefined |
223
+ | `sky.preetham(options: AuraSkyPreethamOptions)` | `AuraNodeBuilder<AuraSceneNode>` | options: exposure?: number \| undefined; moon?: unknown; groundColor?: string \| undefined; sun: AuraSkySunSpec; turbidity?: number \| undefined; rayleigh?: number \| undefined; mieCoefficient?: number \| undefined; mieDirectionalG?: number \| undefined; stars?: AuraSkyStarsOptions \| und…; clouds?: AuraSkyCloudsOptions \| un…; name?: string \| undefined; captureEnvironment?: boolean \| undefined; affectsFog?: boolean \| undefined |
224
+ | `sky.gradient(options: AuraSkyGradientOptions)` | `AuraNodeBuilder<AuraSceneNode>` | options: intensity?: number \| undefined; moon?: unknown; clouds?: AuraSkyCloudsOptions \| un…; sun?: AuraSkySunSpec \| undefined; zenith: string; horizon: string; ground?: string \| undefined; exponent?: number \| undefined; horizonGlow?: number \| undefined; bands?: readonly AuraSkyBand[] \| …; stars?: AuraSkyStarsOptions \| und…; name?: string \| undefined; captureEnvironment?: boolean \| undefined; affectsFog?: boolean \| undefined |
219
225
  | `sky.hdri(options: {…})` | `AuraNodeBuilder<AuraSceneNode>` | options: texture: AuraAssetRef<"texture">; intensity?: number \| undefined; rotation?: number \| undefined; blurriness?: number \| undefined; name?: string \| undefined; captureEnvironment?: boolean \| undefined; affectsFog?: boolean \| undefined |
220
226
  | `sky.dayNight(options?: {…})` | `{ readonly nodes: readonly AuraSceneNode[]; readonly background: stri…` | options: hour?: number \| undefined; seed?: number \| undefined; starCount?: number \| undefined; cloudCellCount?: number \| undefined; starLimit?: number \| undefined; cloudLimit?: number \| undefined; model?: "preetham" \| "gradient" \|… |
221
227
 
@@ -231,7 +237,7 @@ void primitives.cylinder({ name: "plinth", size: [2, 0.9, 2], material: material
231
237
  |---|---|---|
232
238
  | `primitives.box(options: AuraPrimitiveOptions \| undefined)` | `AuraNodeBuilder<AuraPrimitiveNode>` | options: name?: string \| undefined; material?: AuraMaterialSpec \| undefi…; size?: number \| AuraVec3 \| undef…; castShadow?: boolean \| undefined; receiveShadow?: boolean \| undefined; physics?: AuraNodePhysicsSpec \| und…; instances?: readonly AuraTransformSpe…; instanceColors?: readonly string[] \| undef…; geometry?: AuraCustomGeometrySpec \| …; text3D?: Omit<AuraText3DGeometry, …; lod?: AuraRootLodSpec \| undefin…; params?: AuraPrimitiveParams \| und…; position?: AuraVec3 \| undefined; pivot?: AuraVec3 \| undefined; rotation?: Aur… |
233
239
  | `primitives.sphere(options: AuraPrimitiveOptions \| undefined)` | `AuraNodeBuilder<AuraPrimitiveNode>` | same as `primitives.box` |
234
- | `primitives.plane(options: AuraPrimitiveOptions \| undefined)` | `AuraNodeBuilder<AuraPrimitiveNode>` | same as `primitives.box` |
240
+ | `primitives.plane(options: AuraPlaneOptions \| undefined)` | `AuraNodeBuilder<AuraPrimitiveNode>` | options: position?: AuraVec3 \| undefined; layers?: number \| undefined; physics?: AuraNodePhysicsSpec \| und…; name?: string \| undefined; pivot?: AuraVec3 \| undefined; rotation?: AuraVec3 \| undefined; scale?: number \| AuraVec3 \| undef…; lookAt?: AuraVec3 \| undefined; rotationOrder?: AuraEulerOrder \| undefined; quaternion?: AuraQuat \| undefined; renderOrder?: number \| undefined; frustumCulled?: boolean \| undefined; userData?: Record<string, unknown> \|…; castShadow?: boolean \| undefined; material?: AuraMaterialSpec \| undefi…; … |
235
241
  | `primitives.cylinder(options: AuraPrimitiveOptions \| undefined)` | `AuraNodeBuilder<AuraPrimitiveNode>` | same as `primitives.box` |
236
242
  | `primitives.capsule(options: AuraPrimitiveOptions \| undefined)` | `AuraNodeBuilder<AuraPrimitiveNode>` | same as `primitives.box` |
237
243
  | `primitives.torus(options: AuraPrimitiveOptions \| undefined)` | `AuraNodeBuilder<AuraPrimitiveNode>` | same as `primitives.box` |
@@ -278,7 +284,7 @@ void effects.bloom({ intensity: 0.7, threshold: 0.75, radius: 0.55 });
278
284
  | `effects.vignette(options?: PostV3EffectOptions)` | `AuraNodeBuilder<AuraEffectNode>` | |
279
285
  | `effects.filmGrain(options?: PostV3EffectOptions)` | `AuraNodeBuilder<AuraEffectNode>` | |
280
286
  | `effects.chromaticAberration(options?: PostV3EffectOptions)` | `AuraNodeBuilder<AuraEffectNode>` | |
281
- | `effects.shaderPass(options: AuraShaderPassOptions)` | `AuraCustomPostPass` | options: name?: string \| undefined; glsl?: string \| undefined; wgsl?: string \| undefined; fragment?: { readonly glsl?: string;…; uniforms?: Readonly<Record<string, n…; inputs?: readonly ("depth" \| "norm…; insertAt?: PostInsertAt \| undefined; order?: AuraPostPassOrder \| undef…; enabled?: boolean \| undefined |
287
+ | `effects.shaderPass(options: AuraShaderPassOptions)` | `AuraCustomPostPass` | options: name?: string \| undefined; glsl?: string \| undefined; wgsl?: string \| undefined; fragment?: { readonly glsl?: string;…; uniforms?: Readonly<Record<string, n…; inputs?: readonly ("depth" \| "colo…; insertAt?: PostInsertAt \| undefined; order?: AuraPostPassOrder \| undef…; enabled?: boolean \| undefined |
282
288
  | `effects.fog(options?: FogNodeOptions)` | `AuraNodeBuilder<AuraEffectNode>` | |
283
289
  | `effects.cinematicBloom(options?: Omit<AuraEffectNode, "kind" \| "effect">)` | `AuraNodeBuilder<AuraEffectNode>` | |
284
290
  | `effects.volumetricFog(options?: AuraVolumetricFogOptions)` | `AuraNodeBuilder<AuraEffectNode>` | |
@@ -328,6 +334,31 @@ void instances.cone({ name: "pines", transforms: [{ position: [0, 1, 0], scale:
328
334
  | `instances.custom(spec: AuraCustomGeometrySpec, options: Omit<AuraInstancedPrimitiveOptions, "geometry">)` | `AuraNodeBuilder<AuraPrimitiveNode>` | spec: kind: "aura-custom-geometry"; positions: AuraVec3Attribute; normals?: AuraVec3Attribute \| undef…; uvs?: AuraVec2Attribute \| undef…; uv1s?: AuraVec2Attribute \| undef…; colors?: readonly string[] \| reado…; indices: AuraIndexAttribute; attributes?: Readonly<Record<string, A…; bounds?: { readonly min: AuraRootV…; groups?: readonly AuraGeometryGrou…; drawRange?: AuraGeometryDrawRangeSpec…; morphTargets?: readonly AuraGeometryMorp…; topology?: "triangles" \| "lines" \| "…; dynamic?: boolean \| undefined |
329
335
  | `instances.model(asset: TAsset, options: {…})` (+1 overloads) | `AuraNodeBuilder<AuraGroupNode>` | options: name?: string \| undefined; material?: AuraMaterialSpec \| undefi…; castShadow?: boolean \| undefined; receiveShadow?: boolean \| undefined; visible?: boolean \| undefined; role?: AuraModelRole \| undefined; scaleMode?: AuraModelScaleMode \| unde…; targetHeight?: number \| undefined; targetMaxDimension?: number \| undefined; targetLength?: number \| undefined; physics?: AuraNodePhysicsSpec \| und…; hiddenNodeNames?: readonly string[] \| undef…; materialOverrides?: readonly AuraModelMateria…; variant?: string \| undefined; lod?… |
330
336
 
337
+ ## `points`
338
+
339
+ Static cloud. `Float32Array` positions + packed `Uint8Array`/`Uint32Array`/`Float32Array` colors stay typed (no per-point objects, no color parsing). Live clouds: `points.stream` + `pointStreamOf(handle)` (docs/agents/streaming-buffers.md).
340
+
341
+ ```ts
342
+ void points({ positions: new Float32Array([0, 0, 0, 1, 1, 1]), colors: new Uint8Array(8).fill(255), size: 2 });
343
+ ```
344
+
345
+ | Call | Returns | Options |
346
+ |---|---|---|
347
+ | `points(options: AuraPointsOptions)` | `AuraNodeBuilder<AuraPointsNode>` | options: points?: AuraLinePositionsInput \| …; positions?: AuraLinePositionsInput \| …; colors?: readonly string[] \| Float…; size?: number \| undefined; sizeAttenuation?: boolean \| undefined; map?: AuraMaterialTextureInput …; alphaTest?: number \| undefined; transparent?: boolean \| undefined; material?: AuraMaterialSpec \| undefi…; name?: string \| undefined; visible?: boolean \| undefined; runtime?: AuraRuntimeNodeSpec \| und…; position?: AuraVec3 \| undefined; rotation?: AuraVec3 \| undefined; scale?: number \| AuraVec3 \| undef… |
348
+ | `points.stream(spec: AuraPointStreamSpec)` | `AuraNodeBuilder<AuraPointsNode>` | spec: capacity: number; colors?: AuraPointStreamColorForma…; colorSpace?: "linear" \| "srgb" \| undef…; size?: number \| undefined; sizeAttenuation?: boolean \| undefined; map?: AuraMaterialTextureInput …; alphaTest?: number \| undefined; transparent?: boolean \| undefined; material?: AuraMaterialSpec \| undefi…; bounds?: { readonly min: AuraVec3;…; name?: string \| undefined; visible?: boolean \| undefined; runtime: AuraRuntimeNodeSpec; position?: AuraVec3 \| undefined; rotation?: AuraVec3 \| undefined; scale?: number \| AuraVec3 … |
349
+
350
+ ## `pointStreamOf`
351
+
352
+ Narrows `app.nodes.require(id)` of a `points.stream({ capacity, colors?, runtime: { id } })` node to `{ write, lease, setDrawCount, drawCount, capacity, stats }`; typed in-place updates, one dirty-range upload.
353
+
354
+ ```ts
355
+ void pointStreamOf;
356
+ ```
357
+
358
+ | Call | Returns | Options |
359
+ |---|---|---|
360
+ | `pointStreamOf(handle: AuraRuntimeNodeHandle \| undefined)` | `AuraPointStreamHandle` | |
361
+
331
362
  ## `geometry`
332
363
 
333
364
  `define` builds the spec; mount it with `geometry.custom(spec, { material })`.
@@ -374,7 +405,7 @@ void interactions.orbit({ target: "crate" });
374
405
 
375
406
  ## `physics`
376
407
 
377
- Rapier-backed worlds, bodies and colliders; see `docs/api/` physics pages before use.
408
+ Rapier-backed worlds, bodies and colliders; see `docs/api/` physics pages before use. Needs `npm install @dimforge/rapier3d-compat@0.20.0` (optional peer; once installed, every app that imports the engine loads its 1.08 MB gzip chunk at startup unless it is built with `aura3d()` from `@aura3d/assets/vite`, which leaves Rapier out of apps that never mention physics, or `aura3d({ physics: "lazy" })`, which downloads it on first use (then `await app.physics.ready?.()` before imperative calls); see `docs/concepts/physics.md`): without it you get the typed `PHYSICS_BACKEND_MISSING` error (`error.code`, `error.installCommand`) in `diagnostics().errors`, on the canvas, from `app.physics`, from `preloadPhysics()`, and from `aura3d doctor`. Working example: recipe 16 (`recipes-src/physics-basics`).
378
409
 
379
410
  ```ts
380
411
  void physics.box;
@@ -420,6 +451,18 @@ void AnimationController;
420
451
  | `AnimationController#listClips()` | `readonly AuraRegisteredAnimationClip<TClipId, TEvent>[]` | |
421
452
  | `AnimationController#dispose()` | `void` | |
422
453
 
454
+ ## `createSpatialPicker`
455
+
456
+ CPU picking for `points` (nearest to the ray within a pixel or world tolerance) and instanced nodes (per-instance box or sphere). `picker.pick({ camera, ndc }, { viewport, pixelRadius })`; `picker.invalidate(name)` after writing a position or matrix buffer in place. `bindSpatialPicking` adds hover and click.
457
+
458
+ ```ts
459
+ const picker = createSpatialPicker({ nodes: [] });
460
+ ```
461
+
462
+ | Call | Returns | Options |
463
+ |---|---|---|
464
+ | `createSpatialPicker(sourcesOf: SpatialPickerSources \| (() => SpatialPickerSources))` | `SpatialPicker` | |
465
+
423
466
  ## `createFixedStepLoop`
424
467
 
425
468
  Deterministic simulation loop for games.
@@ -1,6 +1,6 @@
1
1
  # Recipes: copy a working scene, then adapt it
2
2
 
3
- Coming from three.js? Read [`threejs-to-aura3d.md`](threejs-to-aura3d.md) first; signatures are in [`api-signatures.md`](api-signatures.md). Recipes 8 to 15 build complete scenes with no admitted models. Start here when the task looks like one of these. Each recipe is a complete `src/main.ts` body that compiled against the engine types (`tsc`, strict) and uses only non-deprecated root exports. Copy it, change names and numbers, run `npm run build`. Do not read `node_modules/@aura3d/engine/dist` to discover the API first: that is the slowest path (in the cold-start trial, 8 of 15 runs spent all 40 turns reading `.d.ts` and `dist` files without writing a scene, while the game-loop runs, which had a complete sample to copy, finished in 11 to 17 turns).
3
+ Coming from three.js? Read [`threejs-to-aura3d.md`](threejs-to-aura3d.md) first; signatures are in [`api-signatures.md`](api-signatures.md). Recipes 8 to 15 build complete scenes with no admitted models; recipe 16 covers physics. Start here when the task looks like one of these. Each recipe is a complete `src/main.ts` body that compiled against the engine types (`tsc`, strict) and uses only non-deprecated root exports. Copy it, change names and numbers, run `npm run build`. Do not read `node_modules/@aura3d/engine/dist` to discover the API first: that is the slowest path (in the cold-start trial, 8 of 15 runs spent all 40 turns reading `.d.ts` and `dist` files without writing a scene, while the game-loop runs, which had a complete sample to copy, finished in 11 to 17 turns).
4
4
 
5
5
  Page needs a `<div id="app">` (or a `<canvas id="app">`); `createAuraApp("#app", { scene })` mounts into it. Background, camera, lights and fog are ordinary nodes or scene options. Animate inside `app.onFrame((frame) => ...)` using `frame.dt`. Await `app.ready()` when you need the first frame. With a HUD or other elements beside the canvas, give the canvas a wrapper of definite size (`<div id="stage" style="width:640px;height:360px"><canvas id="app"></canvas></div>`), or pass `resize: false` and a `width`/`height` on the canvas; the app sizes itself to its parent, and an auto-height parent that also holds a HUD can make a canvas grow until it draws nothing on 4.1.3 and earlier (see troubleshooting, black frame checklist).
6
6
 
@@ -720,6 +720,98 @@ const app = createAuraApp("#app", { scene: stage });
720
720
  await app.ready();
721
721
  ```
722
722
 
723
+ ## 16. Physics: bodies, a stacked wall, a swinging rope, collision events, fixed step
724
+
725
+ - Goal: `.physics({...})` bodies on nodes (static ground, a 5 x 4 crate wall), a ball on a `ball-socket` rope from a gantry pivot that swings into the wall, `app.physics.onCollision`, and a fixed 1/60 step so the result depends on the step count only. `?steps=140` freezes the simulation after 140 steps for captures.
726
+ - Install first: `npm install @dimforge/rapier3d-compat@0.20.0`. Rapier is an optional peer, so it is only in your build if you install it; once installed, every app that imports `@aura3d/engine` downloads and initialises it at startup, even one that never calls physics (a 2.85 MB chunk, 1.08 MB gzip, measured; the `aura3d-no-tla` condition does not avoid it). Build with `aura3d()` from `@aura3d/assets/vite` and Vite leaves Rapier out of any app whose code never mentions physics; `aura3d({ physics: "lazy" })` keeps it but downloads it on first use, in which case add `await app.physics.ready?.()` before the imperative `app.physics.createBody` / `createJoint` calls below (`docs/concepts/physics.md`). Without it the app does not crash silently: `diagnostics().errors` and the canvas show `PHYSICS_BACKEND_MISSING` with this install command, `app.physics` throws the same typed error, and `aura3d doctor` flags the project.
727
+ - Final frame: the ball has hit the wall at the base of a dark gantry and the crates are tumbling into a heap on green ground.
728
+ - Replaces in three.js: a Rapier/cannon setup plus a manual body-to-mesh sync loop (here `physics: { bindNodes: true }` plus `.runtime({ id })` on each node does the sync).
729
+ - Values: gravity `[0, -9.81, 0]`, step 1/60 with at most 5 catch-up steps, ball mass 6 radius 0.55, crates mass 1, friction 0.7.
730
+ - Capture: `recipes-src/physics-basics/frame.png`; source: `recipes-src/physics-basics/main.ts`.
731
+
732
+ ```ts
733
+ import { camera, createAuraApp, lights, material, primitives, scene, type AuraNodeBuilder, type AuraPrimitiveNode } from "@aura3d/engine";
734
+
735
+ // Rigid bodies on scene nodes: a static ground, a stacked wall of crates, a ball on a ball-socket rope that swings
736
+ // into the wall (the wrecking ball), collision events, and a deterministic fixed step. Needs the Rapier backend
737
+ // (`npm install @dimforge/rapier3d-compat@0.20.0`); without it the app reports PHYSICS_BACKEND_MISSING in
738
+ // diagnostics().errors and on the canvas. ?steps=N freezes the simulation after N fixed steps for a capture.
739
+ const STEP = 1 / 60; // fixed step: the result depends on the step count, never on the display rate
740
+ const MAX_CATCH_UP = 5;
741
+ const freezeAt = new URLSearchParams(location.search).get("steps");
742
+
743
+ const CRATE = 0.5;
744
+ const COLORS = ["#c98b4b", "#b8763a", "#d9a066", "#a86a32"];
745
+ const crates: AuraNodeBuilder<AuraPrimitiveNode>[] = [];
746
+ for (let row = 0; row < 5; row += 1) {
747
+ for (let col = 0; col < 4; col += 1) {
748
+ crates.push(
749
+ primitives.box({ name: `crate-${row}-${col}`, size: CRATE, material: material.pbr({ color: COLORS[(row + col) % COLORS.length]!, roughness: 0.8 }) })
750
+ .position(0.2 + (row % 2) * 0.02, CRATE / 2 + row * CRATE, (col - 1.5) * CRATE)
751
+ .runtime({ id: `crate-${row}-${col}` })
752
+ .physics({ type: "dynamic", shape: "box", halfExtents: [CRATE / 2, CRATE / 2, CRATE / 2], mass: 1, friction: 0.7, restitution: 0.05 })
753
+ );
754
+ }
755
+ }
756
+
757
+ const PIVOT: [number, number, number] = [-1.2, 6.2, 0]; // rope anchor; the ball starts 5 m away on the arc and falls into the wall
758
+ const BALL_START: [number, number, number] = [-4.2, 2.2, 0];
759
+
760
+ const app = createAuraApp("#app", {
761
+ physics: { gravity: [0, -9.81, 0], bindNodes: true, seed: 7 },
762
+ scene: scene({ background: "#8fb4d8" })
763
+ .add(lights.hemisphere({ skyColor: "#cfe2f6", groundColor: "#4a5a3a", intensity: 1.2 }))
764
+ .add(lights.directional({ name: "sun", position: [6, 9, 5], target: [0, 0.5, 0], intensity: 4, color: "#fff0d8", shadow: true }))
765
+ .add(
766
+ primitives.box({ name: "ground", size: [30, 0.5, 14], material: material.pbr({ color: "#6f8a50", roughness: 1 }) })
767
+ .position(0, -0.25, 0)
768
+ .runtime({ id: "ground" })
769
+ .physics({ type: "static", shape: "box", halfExtents: [15, 0.25, 7], friction: 0.9 })
770
+ )
771
+ .add(primitives.box({ name: "gantry", size: [8, 0.25, 0.4], material: material.pbr({ color: "#394457", metallic: 0.6, roughness: 0.4 }) }).position(PIVOT[0], PIVOT[1] + 0.125, 0))
772
+ .add(primitives.box({ name: "post-left", size: [0.3, PIVOT[1], 0.4], material: material.pbr({ color: "#394457", metallic: 0.6, roughness: 0.4 }) }).position(PIVOT[0] - 3.85, PIVOT[1] / 2, 0))
773
+ .add(primitives.box({ name: "post-right", size: [0.3, PIVOT[1], 0.4], material: material.pbr({ color: "#394457", metallic: 0.6, roughness: 0.4 }) }).position(PIVOT[0] + 3.85, PIVOT[1] / 2, 0))
774
+ .addMany(crates)
775
+ .add(
776
+ primitives.sphere({ name: "ball", size: 1.1, material: material.pbr({ color: "#d94b3d", metallic: 0.4, roughness: 0.35 }) })
777
+ .position(...BALL_START)
778
+ .runtime({ id: "ball" })
779
+ .physics({ type: "dynamic", shape: "sphere", radius: 0.55, mass: 6, restitution: 0.1, friction: 0.5 })
780
+ )
781
+ .camera(camera.perspective({ position: [4, 3.6, 13], target: [-0.8, 2.7, 0], fov: 46, near: 0.1, far: 200 }))
782
+ });
783
+
784
+ // The anchor is a body-less pivot: a static body with no node, and the rope is a ball-socket joint through the
785
+ // world-space anchor point above the ball (anchors are world space, bodies keep their local frames).
786
+ app.physics.createBody({ name: "pivot", type: "static", shape: "sphere", radius: 0.05, position: PIVOT, sensor: true });
787
+ app.physics.createJoint({ kind: "ball-socket", bodyA: "pivot", bodyB: "ball", anchor: PIVOT });
788
+
789
+ // Collision events: count solid hits and remember the hardest one. The page object is what the render gate reads.
790
+ const stats = { steps: 0, solidHits: 0, hardestHit: 0, firstCrateHitStep: -1 };
791
+ app.physics.onCollision((event) => {
792
+ if (event.phase !== "begin" || event.sensor) return;
793
+ stats.solidHits += 1;
794
+ stats.hardestHit = Math.max(stats.hardestHit, event.relativeSpeed);
795
+ const names = [event.nodeA, event.nodeB];
796
+ if (stats.firstCrateHitStep < 0 && names.includes("ball") && names.some((name) => name?.startsWith("crate-"))) stats.firstCrateHitStep = stats.steps;
797
+ });
798
+
799
+ const frozen = freezeAt === null ? undefined : Math.max(0, Math.floor(Number(freezeAt)));
800
+ let accumulator = 0;
801
+ const stepOnce = (): void => { app.physics.step(STEP); stats.steps += 1; };
802
+ if (frozen !== undefined) for (let i = 0; i < frozen; i += 1) stepOnce();
803
+ app.onFrame((frame) => {
804
+ if (frozen !== undefined) return;
805
+ accumulator = Math.min(accumulator + frame.dt, STEP * MAX_CATCH_UP);
806
+ while (accumulator >= STEP) { stepOnce(); accumulator -= STEP; }
807
+ });
808
+
809
+ const positions = (): Record<string, readonly number[]> =>
810
+ Object.fromEntries(["ball", "crate-0-0", "crate-4-2"].map((name) => [name, app.physics.bodies.require(name).position().map((value) => Math.round(value * 1e4) / 1e4)]));
811
+ (window as unknown as { __PHYSICS_RECIPE__: unknown }).__PHYSICS_RECIPE__ = { stats, positions, diagnostics: () => app.diagnostics().errors };
812
+ await app.ready();
813
+ ```
814
+
723
815
  ## Type traps (strict `tsc` errors agents hit on a first draft)
724
816
 
725
817
  | Error | Cause | Fix |
@@ -5,13 +5,13 @@ Read this first. Aura3D is a standalone engine with its own renderer: you descri
5
5
  | If you would write (three.js) | Write (Aura3D) | Difference that bites |
6
6
  | --- | --- | --- |
7
7
  | `Scene` + `PerspectiveCamera` + `WebGLRenderer` + `setAnimationLoop` | `scene({ background }).camera(camera.perspective({ position, target, fov }))` then `createAuraApp("#app", { scene })`; per frame `app.onFrame((f) => ...)` with `f.dt`; `await app.ready()` | No renderer, resize or loop code. Mount target is a `<canvas>` or a sized wrapper. |
8
- | `new Mesh(geometry, material)` | `primitives.box({ name, size: [w, h, d], material: material.pbr({ color }) })` (also `sphere`, `plane`, `cylinder`, `capsule`, `cone`, `torus`, `torusKnot`, `lathe`, `extrude`, `tube`) | Primitives are unit shapes scaled by `size`. `sphere` size is the **diameter**; `plane` is `[w, 1, d]` and lies in XZ. Unknown option keys are reported, not ignored. |
9
- | `mesh.position.set(...)`, `rotation`, `scale` | Build time: `.position(x, y, z).rotate(x, y, z).scale(2)`. Runtime: `.runtime({ id, mutable: true })`, then `app.nodes.require(id).setPosition(x, y, z)`, `.setRotation`, `.setScale` | A node you move later needs `runtime({ id, mutable: true })`. Handles have `set*`, not `.position.x`. Vectors are tuples, not `Vector3`. |
8
+ | `new Mesh(geometry, material)` | `primitives.box({ name, size: [w, h, d], material: material.pbr({ color }) })` (also `sphere`, `plane`, `cylinder`, `capsule`, `cone`, `torus`, `torusKnot`, `lathe`, `extrude`, `tube`) | Primitives are unit shapes scaled by `size`. `sphere` size is the **diameter**; `plane` is `[w, 1, d]` and lies in XZ (`size: [w, d]` is accepted and means the same). Unknown option keys are reported, not ignored. |
9
+ | `mesh.position.set(...)`, `rotation`, `scale` | Build time: `.position(x, y, z).rotate(x, y, z).scale(2)`. Runtime: `.runtime({ id, mutable: true })`, then `app.nodes.require(id).setPosition(x, y, z)`, `.setRotation`, `.setScale` | A node you move later needs `runtime({ id, mutable: true })`. Handles have `set*`, not `.position.x`. Vectors are tuples, not `Vector3`. Builder `.rotation(x, y, z)` is accepted as `.rotate(x, y, z)`. |
10
10
  | `Group` / `Object3D` hierarchy, bones as groups | `group("arm", [children], { position }).runtime({ id, mutable: true })` | Children use local transforms; a group's origin is its pivot. |
11
11
  | `BufferGeometry` + `setAttribute` + `setIndex` | `geometry.custom(geometry.define({ positions, normals, uvs, colors, indices, bounds }), { name, material })` | Typed arrays in; `bounds` required; `colors` are per-vertex RGBA. `geometry.dynamic` for geometry you rewrite. |
12
- | `MeshStandardMaterial` | `material.pbr({ color, roughness, metallic, emissive, opacity })` | Also `physical` (clearcoat), `emissive`, `toon`, `matcap`. Colour is a CSS string. |
12
+ | `MeshStandardMaterial` | `material.pbr({ color, roughness, metallic, emissive, opacity })` | Also `physical` (clearcoat), `emissive`, `toon`, `matcap`. Colour is a CSS string. `side: "double"` means `doubleSided: true`; `transparent`, `wireframe`, `flatShading` have no equivalent and are reported (see [`guessable-api.md`](guessable-api.md)). |
13
13
  | `ShaderMaterial` | `material.custom({ glsl: { vertex, fragment }, uniforms: { t: { type: "f32", value: 0 } } })`; `handle.setUniform("t", v)` | GLSL 3.00 (`in`/`out`, write `outColor`). Prelude names: `position`, `normal`, `uv`, `modelViewProjectionMatrix`, `modelMatrix`, `cameraPosition`, `uTime` (wall-clock seconds; advances only while the app redraws every frame: give the node `.runtime({ id, mutable: true })` or register `app.onFrame(...)`, else a static scene draws once and `uTime` looks frozen). Node/TSL: `material.node`. |
14
- | `DirectionalLight` + `castShadow` | `lights.directional({ position, target, intensity, shadow: true })` | `shadow: true` on the light; meshes take `castShadow` / `receiveShadow`. Also `point`, `spot`, `hemisphere`, `ambient`, `rect`. |
14
+ | `DirectionalLight` + `castShadow` | `lights.directional({ position, target, intensity, shadow: true })` | `shadow: true` on the light (`castShadow: true` is accepted as the same); meshes take `castShadow` / `receiveShadow`. Also `point`, `spot`, `hemisphere`, `ambient`, `rect`. |
15
15
  | `scene.environment = pmrem` | `environments.neutral({ intensity, background: false })` (also `studio`, `outdoor`, `hdri`, `preset`) | Metals render dark without an environment. `looks.preset(id)` bundles environment, key light, fog and grade. |
16
16
  | `scene.background = sky shader`, `Sky` | `sky.preetham({ sun: { elevationDeg, azimuthDeg } })`, `sky.gradient`, `sky.dayNight` | Move the sun live with `app.atmosphere.setSky({ sun })`. |
17
17
  | `scene.fog = new FogExp2` | `effects.fog({ color, density })` | A scene node, not a property. |
@@ -25,6 +25,8 @@ Read this first. Aura3D is a standalone engine with its own renderer: you descri
25
25
  | `geometry.dispose()`, `renderer.dispose()` | `app.dispose()` | One call disposes everything the app made. |
26
26
  | `Vector3`, `Clock` | `[x, y, z]` tuples at option sites; `Clock` from `@aura3d/engine`; `Vector3` from `@aura3d/math/three` | Mutable three.js-shaped math lives only in `@aura3d/math/three`. |
27
27
 
28
- Not here: `GLTFLoader` (use a typed asset, `model(assets.x)`; see [`asset-workflow.md`](asset-workflow.md)), `renderer.toneMapping` (use the look preset's `output`), custom render targets and `EffectComposer` chains (no equivalent; check `docs/migration/from-threejs.md`).
28
+ Guessed names: every three.js spelling that models actually tried, with the decision for each (alias, reported with a suggestion, or documented), is in [`guessable-api.md`](guessable-api.md). Unknown builder options are reported once with a "did you mean" suggestion (`OPTION_IGNORED`; strict mode throws).
29
+
30
+ Not here: `GLTFLoader` (use a typed asset, `model(assets.x)`; see [`asset-workflow.md`](asset-workflow.md)), `renderer.toneMapping` (use the look preset's `output`, or `output: { toneMapping: "aces" }`; `"aces-filmic"` is not a name), custom render targets and `EffectComposer` chains (no equivalent; check `docs/migration/from-threejs.md`).
29
31
 
30
32
  Porting an existing file: `npx @aura3d/cli@latest migrate three "src/**/*.ts"` lists what maps, what is approximate and what has no equivalent; it is a worklist, not a drop-in conversion. Full mapping with verdicts: [`docs/migration/from-threejs.md`](../migration/from-threejs.md).
@@ -53,16 +53,16 @@ Step 1, presets (always try first):
53
53
  `environments.productHero`, `environments.nightCinematic`,
54
54
  `environments.metalStudio`, `environments.glassStudio`, or
55
55
  `environments.forMaterial("metal" | "glass" | "product" | "studio")`.
56
+ Metal and glass that must read: [metal-glass](references/metal-glass.md) (`material.studioMetal`, `material.studioGlass`, softbox).
56
57
  3. Outdoor: `sky.dayNight({ hour })` returns `nodes`, `background`, and
57
58
  `dayFactor`; `weather.precipitation({ type: "rain" | "snow" })` and
58
59
  `weather.wetGround(...)`; `water.surface({ preset: "calm" | "moderate" |
59
60
  "rough" | "storm" })`. Add their `nodes` with `scene().addMany(...)` and use
60
61
  the returned `background`. Strata, caustics, god rays, beams: [visual helpers](references/visual-helpers.md).
61
62
  4. Choosing values: start from `material.presets()` and the named builders
62
- above (car paint is `material.clearcoatPaint`, thin glass is
63
- `material.glass`, brushed metal is `material.brushedMetal`); the in-repo
64
- preset library (`GAME_READY_MATERIAL_PRESETS` in
65
- `packages/engine/src/devtools/materials/MaterialValidation.ts`) is a source
63
+ above (car paint is `material.clearcoatPaint`, thin glass `material.glass`,
64
+ brushed metal `material.brushedMetal`); `GAME_READY_MATERIAL_PRESETS` in
65
+ `packages/engine/src/devtools/materials/MaterialValidation.ts` is a source
66
66
  reference for values, not an import.
67
67
 
68
68
  Step 2, a licensed local file (Poly Haven or another CC0 / CC-BY source you
@@ -0,0 +1,32 @@
1
+ # Metal and glass that must read
2
+
3
+ Use `material.studioMetal({ color, roughness })` and `material.studioGlass({ tint, ior, thickness })` for product shots,
4
+ trophies, spheres and anything where chrome, steel, gold or clear glass is the subject. They carry sensible reflection
5
+ settings (full metalness, no clearcoat, `envMapIntensity` 1 for metal and 1.25 for glass, absorption tint and
6
+ `thickness` for glass), wrap `material.metal` and `material.glass`, and every option still overrides.
7
+
8
+ Why they exist: chrome and glass only show what they reflect or refract. A near-uniform probe (`environments.neutral()`,
9
+ plain `environments.studio()`) plus a pale backdrop leaves both invisible, and raising `envMapIntensity` only brightens
10
+ the same flat reflection. Clear glass additionally needs physical transmission, which is off unless
11
+ `A3D_QR_MATERIALS_TRANSMISSION` is on; without it the glass draws as an opaque white ball and the console prints
12
+ `TRANSMISSION_DEGRADED`.
13
+
14
+ What the helpers do automatically:
15
+
16
+ - A scene that uses either helper and authors no `environments.*` node or look binds the `softbox` environment itself.
17
+ An authored environment or look always wins.
18
+ - `createAuraApp` turns physical transmission on for a scene that uses `material.studioGlass`. A source that sets the
19
+ flag explicitly (`?a3d-qr=-materials.transmission`, `qualityRebuild.flags`) wins. It is decided from the scene given
20
+ to `createAuraApp`; a later `setScene` does not re-decide it.
21
+
22
+ To choose it explicitly: `environments.studio({ preset: "softbox" })`, or `environments.preset("softbox")` (background on).
23
+ `environments.studio()` and `environments.neutral()` without the option render exactly as before.
24
+
25
+ The `softbox` probe is a structured studio: an overhead softbox, a tall key softbox, two strip lights, a windowed fill
26
+ with mullions and two dark flags over a gradient room, at the neutral probe's mean brightness so diffuse lighting
27
+ matches. It is embedded in the package (about 230 KB of base64 in its own lazy chunk), so no `/aura-environments` fetch
28
+ and no copied file; `public/aura-environments/softbox.*` (about 0.5 MiB) ships the same bytes. Regenerate with
29
+ `node_modules/.bin/tsx tools/environments/bake-softbox.ts`.
30
+
31
+ Glass shows the opaque scene behind it, so put it in front of a mid-tone or graded backdrop, not plain white. The helpers
32
+ cannot add a backdrop for you.
@@ -69,6 +69,11 @@ manual quality override invalidates every measurement that follows.
69
69
  `batched.mesh` apply texture maps since 4.1; older builds drew them white.
70
70
  An empty instance array is a no-op with an `INSTANCES_EMPTY` diagnostic, not a
71
71
  crash, so plan for zero placements instead of special-casing them.
72
+ Live data at 10-20 Hz (sensor point clouds, per-frame poses): do not rebuild the scene or tuple
73
+ arrays. Declare `points.stream({ capacity, colors: "u8x4n", runtime: { id } })`, then
74
+ `pointStreamOf(app.nodes.require(id)).write(Float32Array, { colors })`; dense instance writes go
75
+ through `handle.setInstanceMatrices` (primitives and, since 5.1, `instances.model`). See
76
+ `docs/agents/streaming-buffers.md`; its numbers are measurements, not claims.
72
77
  4. Scattered props (trees, rocks, lamps, houses): plan placements with
73
78
  `scatterAlongPath` or `scatterInArea` (seeded, exclusion zones, `minDistance`,
74
79
  slope and height filters), split them by distance with `splitScatterByDistance`
package/skills/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # Aura3D
2
2
 
3
- > Aura3D 5.1.0: a TypeScript SDK for browser 3D apps and games. You write a typed, declarative scene with `@aura3d/engine`; the `@aura3d/cli` toolchain handles typed GLB/glTF assets, checks and deploy gates. Built to be driven by AI coding agents. Site: https://aura3d.auraone.ai
3
+ > Aura3D 5.2.0: a TypeScript SDK for browser 3D apps and games. You write a typed, declarative scene with `@aura3d/engine`; the `@aura3d/cli` toolchain handles typed GLB/glTF assets, checks and deploy gates. Built to be driven by AI coding agents. Site: https://aura3d.auraone.ai
4
4
 
5
5
  ## First screen: Aura3D patterns (copy the shape, change the subject)
6
6
 
@@ -280,4 +280,4 @@ Release integrity rules:
280
280
  - Docs: https://aura3d.auraone.ai/docs/api.html, https://aura3d.auraone.ai/docs/agent-quickstart.html, https://aura3d.auraone.ai/docs/claims.html
281
281
  - Showcase (25 playable games): https://aura3d.auraone.ai/apps/showcase-index/
282
282
  - Sitemap: https://aura3d.auraone.ai/sitemap.xml; crawl policy: https://aura3d.auraone.ai/robots.txt
283
- - Last updated: 2026-11-06
283
+ - Last updated: 2026-11-07
@@ -152,6 +152,10 @@
152
152
  "large-scene": [
153
153
  "aura3d-threejs-migration",
154
154
  "aura3d-performance"
155
+ ],
156
+ "procedural-scene": [
157
+ "aura3d-scene-authoring",
158
+ "aura3d-materials-environments"
155
159
  ]
156
160
  },
157
161
  "clients": {