assets/humanoid/FingerGenerator

Summary: engine/assets/humanoid/FingerGenerator.js — synthesize finger bones from a hand mesh when the source skeleton has none (the browser-friendly geometric approach: detect open-hand fingertip "lobes", not ML). Method: 1. Build a hand frame from the wrist→fingertip direction; the lateral spread axis (fingers fan out) and palm normal (thinnest) come from 2-D PCA of the vertices in the plane perpendicular to forward. 2. Take the far band of vertices (toward the tips) and cluster them along the lateral axis — each cluster is one finger lobe. 3. Label the shortest extreme lobe as the thumb; order the rest index→pinky. 4. Emit a short straight bone chain (proximal→intermediate→distal→tip) per finger. If fewer than four lobes separate (a closed fist / low-poly hand), report `detected:false` so the caller falls back to a single paddle.  Pure geometry, GPU-free, deterministic → unit-testable.


generateFingers

Summary: Generate finger bones from hand vertices.


assets/humanoid/FingerMapper

Summary: engine/assets/humanoid/FingerMapper.js — detect EXISTING finger bones under a hand node by name (Mixamo "LeftHandIndex1..3", VRM "J_Bip_L_Index_Proximal", generic "index_01"), grouping them into canonical fingers with palm→tip joint order. Used before procedural generation: if the artist rigged fingers, keep them; only synthesize when none are found.


mapFingers

Summary: Map existing finger bones under a hand node.


assets/humanoid/HandRig

Summary: engine/assets/humanoid/HandRig.js — hand + finger rig schema (spec §12). A hand is a wrist + up to five fingers, each a short bone chain. Fingers come from one of three sources: the source skeleton's named bones (`skeleton`), procedurally GENERATED from the hand mesh when the skeleton has none (`generated`), or a single paddle bone when the hand is a closed fist / too low-poly (`paddle`).


assets/humanoid/HandRigBuilder

Summary: engine/assets/humanoid/HandRigBuilder.js — build a hand rig for each hand of a humanoid. Priority: (1) use named finger bones if the skeleton has them; else (2) PROBE the hand mesh (vertices skinned to the hand joint) and GENERATE finger bones geometrically; else (3) a single paddle. Wrist + forward come from the humanoid rig (hand world position, and the lowerArm→hand direction).


extractHandVertices

extractHandVertices(model, handNodeId, thresh = 0.4, pose = null)

Summary: Vertices skinned (weight ≥ thresh) to the hand joint, in node-hierarchy SCENE space (so they match the wrist + the rendered mesh). `pose` is a node→world map.


buildHandRig

Summary: Build a single hand rig.


buildHands

Summary: Build both hands for a model that already has a humanoid rig; attaches them at model.rigs.hand = { left, right } and humanoidRig.hands.


assets/humanoid/HumanoidRig

Summary: engine/assets/humanoid/HumanoidRig.js — canonical humanoid bone slots + rig schema (spec §12). A humanoid is NOT its skeleton: it's a mapping from the source skeleton's bones onto a canonical slot set (Unity Mecanim / VRM style) plus rest-pose + correction metadata, so animation can retarget and physics can build a ragdoll regardless of how the artist named or oriented the rig. Every mapping is a HINT with confidence; unmapped/ambiguous bones go to the editor.


createHumanoidRig

Summary: Create a humanoid rig descriptor.


boneSide

Summary: Is a canonical slot a left/right limb (vs a center/spine bone)?


mirrorBone

Summary: The mirrored slot for a left/right bone (e.g. leftHand ↔ rightHand), or null.


assets/humanoid/HumanoidRigBuilder

Summary: engine/assets/humanoid/HumanoidRigBuilder.js — assemble a HumanoidRig from a model's skeleton (spec §12). Maps bones (BoneMapper), snapshots the rest pose, classifies the pose (T vs A) from the arm direction, and flags needsCorrection when required bones are missing — never guessing past the evidence. The rig is the retarget/ragdoll target: it references the source nodes, it does not bake.


buildHumanoidRig

Summary: Build a humanoid rig and attach it at model.rigs.humanoid.


assets/humanoid/SkeletonHeal

Summary: engine/assets/humanoid/SkeletonHeal.js — anatomy-aware, NON-DESTRUCTIVE skeleton repair. When a rig is missing or mislabels core bones, infer them from anatomy (the same heuristics auto-riggers use): • Pelvis = the point between the two hip joints (and the retarget root must be the pelvis, NOT a ground/root-motion bone — so a "Root"/Armature bone at the floor is replaced by the leg-derived pelvis). • Missing spine / chest / neck are interpolated along the hips→head chain. • A missing left/right limb is MIRRORED from the present side across the body's sagittal plane (auto-mirror). Inferred joints are written to `rig.synthesized` (slot → position) — the source model is never modified. `humanoidBonePosition()` returns the real joint if it exists, else the synthesized one.


humanoidBonePosition

Summary: Real or synthesized world position for a canonical slot (or null).


healHumanoid

Summary: Repair a humanoid rig in place (non-destructively): fills `rig.synthesized` with inferred joints and `rig.healed` with a list of what was repaired.


analyzeSymmetry

Summary: Find the skeleton's plane of bilateral symmetry.


healSkeleton

Summary: Heal a generic skeleton by mirroring limbs that exist on only one side.


assets/humanoid/SkinPose

Summary: engine/assets/humanoid/SkinPose.js — scene-space skeleton pose used by rig overlays + hand probing.  IMPORTANT (verified with scripts/probe_skeleton.py across real rigs): the correct space for placing the skeleton so it lands on the RENDERED mesh is the NODE-HIERARCHY world, because the importer/renderer draw the (unskinned) mesh through that same node hierarchy + importTransform. `inverse(IBM)` instead lives in the armature-local / unscaled space (e.g. hips ≈ 0.03 while the node-hierarchy hips ≈ 2.77 with a ×100 mesh-node scale), so using it for the overlay either collapses the skeleton (missing node scale) or double-rotates it (re-applying the armature transform). We therefore use the node hierarchy here; the IBMs are still imported and available (invert4) for true-bind / retarget work later.


Summary: Invert a column-major 4×4 (returns a new Float32Array, or null if singular).


buildBindPose

Summary: Scene-space pose for every node = the node-hierarchy world (TRS composed down the tree). This is exactly the space the renderer draws the mesh in, so the skeleton overlay + hand probing align with the mesh for any export.


jointPosition

Summary: World position of a node in a pose map (or null).


assets/import/Detectors

Summary: engine/assets/import/Detectors.js — hierarchy, part, and semantic detection (spec §6). These produce hints, never assertions: every detection carries a confidence and is editor-correctable. Detection by name is the cheap first pass; geometry-based detection (cylinders → wheels, symmetry, etc.) is layered in by the phase that needs it (vehicles, humanoids, weapons).


detectHierarchy

Summary: Summarize the preserved source hierarchy.


detectParts

Summary: Detect semantic parts by node name. Populates model.detectedParts.


detectSemanticDomain

Summary: Roll detected parts up to a guessed runtime domain (vehicle/humanoid/weapon).


assets/import/FormatDetector

Summary: engine/assets/import/FormatDetector.js — detect format by extension + magic bytes (spec §5). Never trust the filename alone: the magic-byte sniff wins when it disagrees with the extension, and a confidence is reported so the pipeline can fall back to the editor when unsure.


sniffMagic

Summary: Sniff a format from a leading byte slice. Returns a format token or null. Pass at least the first 16 bytes for reliable results.


detectFormat

Summary: Detect a file's format from its name and (optional) leading bytes.


assets/import/GltfSceneImporter

Summary: engine/assets/import/GltfSceneImporter.js — full glTF/GLB scene → EngineModel.  glTF is the preferred runtime delivery format (nodes, meshes, materials, skins, animations, buffers, textures). This importer parses the whole scene graph — not just motion — reusing the engine's accessor toolkit (core/math/MeshAttributeMath) to decode vertex/index data exactly. Materials and textures are preserved as raw blocks here and normalized in Phase 2; the original look must always be recoverable.


parseGlb

Summary: Parse a binary GLB into { json, bin }.


extractGltfMaterialPayload

extractGltfMaterialPayload(json, buffers = [], externalImages = null)

Summary: Extract the recoverable glTF material/texture payload from already-resolved buffers. This is shared by the full asset pipeline and the editor mesh importer so both paths normalize exactly the same source records.


importGltf

Summary: Import a glTF/GLB into an EngineModel.


assets/import/GpuUploader

Summary: engine/assets/import/GpuUploader.js — upload EngineModel primitives to the GPU.  Parsing is GPU-free; this is the separate, device-driven step (spec §9: upload GPU buffers incrementally). Each primitive is interleaved into a standard [position(3), normal(3), uv(2)] vertex layout (generating flat normals when a source lacks them) plus an optional index buffer, so the importer/viewer and the engine renderer can draw it with one shared layout. Buffers are recorded on `primitive.gpu`; `model.gpuReady` flips true when all primitives upload.


standardVertexLayout

Summary: The WebGPU vertex buffer layout matching interleaveStandard().


interleaveStandard

Summary: Interleave a primitive's attributes into the standard layout.


uploadPrimitive

Summary: Upload one primitive to the GPU. Records `primitive.gpu`.


uploadModel

Summary: Upload every primitive in a model. Returns the number uploaded.


releaseModelGpu

Summary: Free GPU buffers held by a model's primitives.


assets/import/ImportPipeline

Summary: engine/assets/import/ImportPipeline.js — orchestrates the ingestion flow (spec §5 pipeline): detect → license-gate → parse → normalize orientation → detect hierarchy/parts/domain → deduplicate/register. GPU upload is a separate explicit step (GpuUploader) because parsing must stay GPU-free and worker-able.


parseToModel

async parseToModel(format, data, opts = {})

Summary: Parse raw bytes of a known format into an EngineModel (no GPU, no registry).


importFile

Summary: Full import of a single file.


assets/import/ObjImporter

Summary: engine/assets/import/ObjImporter.js — Wavefront OBJ (+ optional MTL) → EngineModel.  OBJ is static geometry + simple material references. We preserve groups/objects and per-face material assignment by splitting into one primitive per material group. Polygons are triangulated (fan). MTL parsing is light (names + a few PBR-ish factors) and the full material decode happens in Phase 2.


parseMtl

Summary: Parse a minimal MTL into a map of name → material descriptor.


importObj

Summary: Import OBJ text/bytes into an EngineModel.


assets/import/OrientationResolver

Summary: engine/assets/import/OrientationResolver.js — axis/scale/origin inference (spec §6). Never trust source orientation; never bake corrections into source. We infer a non-destructive ImportTransform (axis correction quat + scale + origin) with a confidence, and a policy decides whether to auto-apply, mark reviewable, preview only, or stay neutral and require manual correction.


resolveOrientation

Summary: Infer an ImportTransform for a model.


assets/import/PlyImporter

Summary: engine/assets/import/PlyImporter.js — Stanford PLY (ASCII + binary LE) → EngineModel.  PLY is a point/mesh utility format. We read the vertex element (position + optional normal/color) and the face element (triangulated), producing one indexed primitive. Big-endian binary is uncommon for web assets and is not supported here (the editor can flag it).


assets/import/StlImporter

Summary: engine/assets/import/StlImporter.js — STL (binary + ASCII) → EngineModel.  STL is CAD/printable part geometry: triangles only, no materials, no hierarchy, no declared units. We import geometry faithfully and leave the unit guess to the OrientationResolver / editor. Output is a single non-indexed primitive with per-vertex positions and face normals.


importStl

Summary: Import STL bytes or text into an EngineModel.


assets/index

Summary: engine/assets/index.js — Global Asset Ingestion Runtime barrel.  Turns any source file into an EngineModel and a usable simulation object: decode → normalize → deduplicate → preserve hierarchy/materials/animations → generate colliders/SDF/rigs/surfaces → cache → spawn.  Build status: [x] Phase 0  foundations (EngineModel, AssetRecord, vpath, canonical space) [x] Phase 1  asset core (dedup, cache/OPFS, registry, resolver, importers) [x] Phase 2  materials & textures (decode, bindings, segments, variants) [x] Phase 3  vehicles (wheel/chassis detect, rig builder, suspension/tire/sync) [x] Render   reusable model rendering (shader/pipeline, GPU drawables, wheel-spin, ModelRenderer) + geometry PCA — shared by demos, editor, and runtime so importing/viewing assets is one call. [ ] Phase 4  render surfaces [ ] Phase 5  humanoids, hands & weapons


assets/material/EngineMaterial

Summary: engine/assets/material/EngineMaterial.js — normalized material + texture binding schemas (spec §7). Materials are decoded, not guessed. Textures bind to surfaces (not loose images); UV sets, sampler wrap/filter, and texture transforms must survive import; packed channels are read exactly. The raw source material is always preserved on `.original` so the imported look stays recoverable even after engine normalization or shader-model approximation.


defaultChannelMap

Summary: glTF default packing: metallic=B, roughness=G, occlusion=R.


createEngineMaterial

Summary: Create a normalized engine material.


createTextureBinding

Summary: Create a texture binding (a texture bound to a material slot on a surface).


hasTextureTransform

Summary: Has this binding a non-identity UV transform (KHR_texture_transform)?


assets/material/MaterialImport

Summary: engine/assets/material/MaterialImport.js — decode source materials into the normalized EngineMaterial (spec §7). Materials are decoded exactly, never guessed: UV sets, sampler wrap/filter, and KHR_texture_transform survive; packed channels are read per the format (metadata wins over filename). The raw material is preserved on `.original` so the imported look stays recoverable.


importMaterials

Summary: Decode every material in a model into EngineMaterial in place. Idempotent: a material already carrying `.original` is left untouched.


assets/material/MaterialVariant

Summary: engine/assets/material/MaterialVariant.js — variants + shader-model fallback (spec §7). Imported materials won't always match the engine shader model, so we normalize into EngineMaterial, can generate a metallic-roughness fallback approximation when needed, and always keep the original recoverable. Variants (e.g. a re-tinted body panel) share the source material and only narrow/adjust.


createMaterialVariant

createMaterialVariant(base, overrides = {})

Summary: Create a material variant that overrides a few fields of a base material while preserving the original (recoverable) and recording the variant lineage.


approximateToMetallicRoughness

Summary: Approximate a non-metallic-roughness material as metallic-roughness for the engine's PBR shader. The original is preserved so the imported look can always be restored. This is a deliberate, labelled approximation — not an exact port.


restoreOriginal

Summary: Restore the original imported material descriptor (the raw source block).


assets/material/SegmentView

Summary: engine/assets/material/SegmentView.js — non-destructive segmentation (spec §8).  We need modular breaking (swap tires, detach doors, tint glass, split a magazine) WITHOUT destroying the source. A SegmentView references original primitive ids / index ranges / materials — it never copies or rewrites geometry. A physical split is only baked later, on export or when a detachable part is actually created. This keeps skeletons, UVs, and morph targets intact.


createSegmentView

createSegmentView(model, mode = SEGMENT_MODE.MATERIAL)

Summary: Create a non-destructive segment view of a model.


bakeSegment

Summary: Bake a single segment into a standalone EngineModel-like payload (only when a detachable part is actually needed — e.g. export). Geometry is copied here, the source model is left untouched.


assets/material/SurfaceBinding

Summary: engine/assets/material/SurfaceBinding.js — bind materials to surfaces (spec §7).  Material assignment happens at the primitive/submesh level, not per-image. A SurfaceBinding records exactly which node/mesh/primitive a material covers, which UV set it uses, and (when known) the semantic part — so segmentation and editing can target real surfaces and bindings survive non-destructive splits.


buildSurfaceBindings

Summary: Build surface bindings for a model (material ↔ primitive). Populates model.surfaceBindings and returns them.


assets/material/TextureImport

Summary: engine/assets/material/TextureImport.js — normalize textures + decode pixels (spec §7). A texture's colour space and usage are inferred from the material slots that bind it (sRGB for colour/emissive, linear for data/normal/ORM), so the same image used as colour vs data is handled correctly. Pixel decode is a separate, browser-only step (createImageBitmap) kept out of parsing. KTX2/ Basis stay compressed here and are routed by TextureManager to the existing native upload or registered decoder/transcoder hook path.


normalizeTextures

Summary: Infer per-texture colour space + usage from the decoded material bindings, and normalize model.textures into self-describing EngineTexture records. Requires importMaterials() to have run first (so bindings exist).


decodeTexture

Summary: Decode a normalized texture's pixels to an ImageBitmap (browser only). Returns the bitmap (also cached on the texture) or null when undecodable (no bytes, external uri, or a compressed format with no transcoder).


decodeAllTextures

Summary: Decode every decodable texture in a model. Returns the count decoded.


assets/material/composite/CompositeMaterialContracts

Summary: Strict, app-neutral contracts for immutable composite material data.


validateCompositeMaterialFacets

validateCompositeMaterialFacets(value, path = '$.facets')


validateCompositeMaterialProvenance

validateCompositeMaterialProvenance({ facets, sources, fieldSources }, path = '$')


validateEngineCompositeMaterialResource

validateEngineCompositeMaterialResource(value, path = '$')

Summary: Validate a complete built-in catalog resource. Returns the input unchanged.


assets/material/composite/EngineCompositeMaterialCatalog

Summary: Browser-native JSON loader. Callers may inject an equivalent fetch boundary.


loadEngineCompositeMaterialCatalog

async loadEngineCompositeMaterialCatalog({ fetchImpl = globalThis.fetch, urls = ENGINE_COMPOSITE_MATERIAL_RESOURCE_URLS, } = {})


assets/material/composite/index


assets/render/ModelGpu

Summary: engine/assets/render/ModelGpu.js — reusable GPU-side helpers to turn an uploaded EngineModel into drawable items. Small, composable, and shared by the importer/viewer, the editor, and the runtime so nobody re-implements texture upload, per-primitive bind groups, world-matrix flattening, or bounds framing.  Pairs with ModelShader (uniform block) and GpuUploader (vertex layout). Pure data in → GPU objects out; callers own the render pass and camera.


createDefaultSampler

Summary: A linear, repeating sampler — a sane default for model albedo.


createWhiteTexture

Summary: A 1×1 white texture so untextured primitives sample white (albedo = color).


makeGpuTexture

Summary: Upload one decoded bitmap to a GPU texture (sRGB for colour, linear for data).


uploadModelTextures

Summary: Decode + upload every embedded texture in a model.


primitiveColor

Summary: Deterministic fallback colour for a primitive lacking a baseColorFactor.


buildModelDrawables

Summary: Build drawable items for an uploaded model: one per primitive, each with its world draw matrix, uniform buffer, bind group, fallback colour, and (optional) wheel-spin descriptor keyed by node id.


bakeSkinnedMeshes

Summary: CPU-skin every skinned mesh to its DEFAULT pose so the static viewer draws the mesh wrapped around the node-hierarchy skeleton (per the glTF rule that a skinned mesh's own node transform is ignored). For each skinned vertex: v' = Σ wⱼ · (jointNodeWorld(j) · inverseBindMatrix(j)) · v   (scene space) Positions/normals are replaced in place (original kept on `_bindPosition`) and the primitive is flagged `_skinnedBaked`. No-op without skins/IBMs. GPU-free.


releaseDrawables

Summary: Free per-item uniform buffers built by buildModelDrawables.


frameBounds

Summary: World-space bounds of a set of drawables, for camera framing.


importMatrixOf

Summary: Compose the standard import-correction matrix from a model's importTransform.


assets/render/ModelRenderer

Summary: engine/assets/render/ModelRenderer.js — the batteries-included way to draw an EngineModel. It composes the small render modules (ModelShader + ModelGpu) and the vehicle wheel-spin helper, applying sensible DEFAULTS so any engine system can go from "I have an EngineModel" to "it's on screen" in two calls:  const r = createModelRenderer(device, { format }); await r.setModel(model, { wheelSpin: true, rig });   // upload + drawables per frame: r.render(pass, { mvp, eye, distance });  The caller still owns the render pass + camera (so it slots into any pipeline); the renderer owns the model's GPU lifetime and offers a depth-texture + camera- framing convenience. Override the shader/format/cull via opts when needed.


assets/render/ModelShader

Summary: engine/assets/render/ModelShader.js — the default lit WGSL shader + pipeline for drawing an EngineModel's standard-layout primitives. Small and overridable: any engine system can grab LIT_MODEL_SHADER for a sensible default, or pass its own WGSL to createModelPipeline. The shader pairs with standardVertexLayout() (pos3 + nrm3 + uv2) and the 160-byte uniform block written by ModelGpu.


createModelPipeline

Summary: Build the render pipeline for EngineModel primitives.


Summary: A neutral physically-plausible lit shader with albedo floor + additive rim.


Summary: Gallery raster lighting derived from the Astral Sculpture Court studies: warm broad key, restrained green-cyan fill, cool portal rim, park/studio environment response, Khronos PBR Neutral, and stable one-LSB dithering. It deliberately preserves the default renderer bind/uniform contract.


Summary: Full-screen gallery/park environment paired with the sculpture shader.


assets/rig/BodyTemplateExport

Summary: engine/assets/rig/BodyTemplateExport.js — turn a generated `CharacterPhysicsAsset` into a starting-point `life.body.v1`-shaped body template JSON (the schema `Life/data/bodies/*.json` + `engine/assets/rig/active-body/BodyAssembler.js` consume).  This mirrors how every mainstream engine handles the generated-vs-hand-authored split (Unreal's Physics Asset auto-generate, Unity's Ragdoll Wizard, Godot's "Create Physical Skeleton"): the generator produces a complete, valid STARTING POINT for skeleton/joint/collision structure — never silently re-applied over hand-tuned data afterward. A dev generates this once for a new creature/character variant, saves it as a plain JSON file, and hand-tunes gameplay-only concerns on top (anatomy/organs, ligaments, muscle model, strength/mass presets) that have no generator and are intentionally hand-authored. This is the "easy for devs to achieve anything" on-ramp: zero-authoring skeleton for a new body, full hand-tuning power afterward.


characterPhysicsAssetToBodyTemplate

characterPhysicsAssetToBodyTemplate(asset, opts = {})

Summary: Convert a `CharacterPhysicsAsset` (from `createCharacterPhysicsAsset()`) into a `life.body.v1` body template — a ready-to-save JSON starting point for a new creature/character variant. Only the skeleton/joint/collision structure is generated; `anatomy`/`ligaments`/`muscleModel`/`presets` are left as explicit, clearly-commented empty stubs since no generator authors those — every mainstream engine's ragdoll generator stops at the same boundary.


assets/rig/CharacterPhysicsAsset

Summary: engine/assets/rig/CharacterPhysicsAsset.js — the single generated packaging layer for a character's physics structure. This is NOT a new generator: it wraps `buildRagdoll()` (RagdollBuilder.js) + `buildJointLimits()` (JointLimits.js) and repackages their output into one serializable schema that every consumer (ActiveRigController/PhysX, RagdollSim, RagdollSkinning, Life's BodyAssembler, RealmForge's compilers, legacy PBDRagdoll wrapper) can read instead of inventing its own bone/joint shape. GPU-free + deterministic.  Schema fields: skeletonFingerprint, boneMap, bodyDefs, jointDefs, jointLimits, massProfile, collisionFilters, driveProfiles, skinningOffsets, particleCageDef (reserved for the future flesh-cage system), validationReport.


computeSkeletonFingerprint

Summary: Stable fingerprint for a bone topology — same skeleton shape (names + parent links, order-independent per bone but position-stable) hashes the same way twice. Used to detect when a cached CharacterPhysicsAsset can be reused vs. must be rebuilt.


createCharacterPhysicsAsset

createCharacterPhysicsAsset(model, opts = {})

Summary: Build a `CharacterPhysicsAsset` for a model. Humanoid path when a humanoid rig is present, otherwise generic topology — identical branch selection to `buildRagdoll()`, which this function delegates to.


validateCharacterPhysicsAsset

Summary: Validate a CharacterPhysicsAsset's internal consistency (indices resolve, joint types known, mass/limit values finite). Follows the same `{ ok, errors, warnings, counts }` pattern as Life's `BodySpecValidator.js`, adapted to this schema's shape.


assets/rig/JointLimits

Summary: engine/assets/rig/JointLimits.js — automatically derive per-joint movement limits (range of motion) from a model, the way Life's BodyAssembler does it: every joint gets a TYPE (ball / hinge / saddle / twist / root) plus a swing cone + twist limit (degrees → radians), and hinge joints get a bend AXIS taken from the bind-pose geometry (perpendicular to the parent→joint→child bend plane). Two paths: • humanoid — role-based anatomical profile keyed by canonical bone slots. • generic  — topology inference (children count + segment straightness) so spiders, dogs, cats, birds, etc. get sensible limits with no template: hub/limb-root joints → ball, straight mid-limb segments → hinge, leaves → fixed. GPU-free + deterministic → gate-testable.


hingeAxis

Summary: Hinge bend axis = normal of the parent→joint→child plane (the axis a knee/elbow actually bends about). Falls back to a perpendicular of the bone direction.


deriveJointType

Summary: Joint type carried by an anatomical profile (ball/hinge/saddle/twist/root).


deriveSwingCone

Summary: Swing-cone half-angle (degrees) carried by a profile.


deriveHingeLimit

Summary: Hinge bend-range (degrees). By Life's hinge convention this value is carried in the profile's `twist` field (a hinge drives one axis), so this is semantically distinct from `deriveTwistLimit` even though both read the same underlying number for a hinge profile.


deriveTwistLimit

Summary: Twist limit (degrees) for a non-hinge (ball/saddle/twist) profile.


mirrorLeftRightLimits

Summary: Verify (and defensively enforce) left/right symmetry between two derived limits — swing/twist degrees must match (anatomy is bilaterally symmetric); axes are expected to differ (mirrored bind-pose geometry) and are left untouched. Explicit, testable version of the symmetry `profileFor()` already guarantees implicitly by stripping the left/right prefix before profile lookup.


inferDriveType

Summary: Infer a coarse drive role (neck/shoulder/elbow/wrist/spineUpper/pelvis/hip/ knee/ankle/toe/default) from a bone/slot name via string matching. Shared so RagdollBuilder, CharacterPhysicsAsset, and future adapters don't each reimplement this heuristic independently.


deriveHumanoidJointLimits

Summary: Derive joint limits for a humanoid rig.


deriveSkeletonJointLimits

Summary: Derive joint limits for ANY skeleton by topology + geometry (spiders, dogs, cats, birds…). No names required.


validJointTypeSet

Summary: The set of valid joint-type strings (`root`/`ball`/`hinge`/`saddle`/`twist`/ `fixed`) as a `Set`, for membership checks — single source of truth so a validator never hand-declares its own copy that can silently drift from `JOINT_TYPE` (this is exactly how `CharacterPhysicsAsset.js` and Life's `BodySpecValidator.js` used to each keep their own disconnected copy).


validateBoneTopology

Summary: Shared bone-topology structural validation — the literally-duplicated core every ragdoll/body validator needs (bone id uniqueness, parent-reference resolution), extracted once so `CharacterPhysicsAsset.validateCharacterPhysicsAsset` and Life's `BodySpecValidator.validateBodyTemplate` can never silently drift apart on these checks again. Each caller still owns its own shape-specific checks (mass/position for CharacterPhysicsAsset; anatomy/ligaments/skeleton extensions for Life).


countTypes

Summary: Count joint types in a limit map (for inspectors/gates).


buildJointLimits

Summary: Auto-derive and attach joint limits to a model. Humanoid path when a humanoid rig is present, otherwise generic topology inference.


deriveBendDirection

Summary: Alias for `hingeAxis` — the joint bend-direction deriver, named for the generic "derive*" family used by RagdollBuilder/CharacterPhysicsAsset.


assets/rig/RagdollBuilder

Summary: engine/assets/rig/RagdollBuilder.js — turn a detected skeleton + auto-derived joint limits into a RAGDOLL descriptor: one capsule body per bone (sized from the bone length, mass from its volume) plus a constrained joint to its parent using the JointLimits type + swing/twist + bend axis. Pure data (no physics engine here) so it can drive `sim/physics/PBDRagdoll` / articulations or the editor's ragdoll. Works for humanoids (canonical bones, incl. healed ones) and for any creature via generic topology. GPU-free + deterministic.


buildHumanoidRagdoll

buildHumanoidRagdoll(model, rig, pose, opts = {})

Summary: Build a ragdoll from a humanoid rig (canonical bones, including healed ones).


buildGenericRagdoll

buildGenericRagdoll(model, pose, opts = {})

Summary: Build a ragdoll from any skeleton by generic topology (spiders, dogs, …).


buildRagdoll

Summary: Build a ragdoll for a model and attach it at model.rigs.ragdoll. Humanoid path when a humanoid rig is present, otherwise generic topology.


assets/rig/RagdollSim

Summary: engine/assets/rig/RagdollSim.js — a tiny, GENERIC Verlet/PBD ragdoll runtime that simulates ANY ragdoll descriptor from RagdollBuilder (humanoid, creature, healed/mirrored limbs). Each bone joint is a particle; each bone is a distance constraint to its parent (Jakobsen "Advanced Character Physics"). Gravity + ground + a few constraint iterations give a stable, believable flop with no dependency on a fixed humanoid template. Pure JS / deterministic / GPU-free.


assets/rig/RagdollSkinning

Summary: engine/assets/rig/RagdollSkinning.js — make the live MESH follow a ragdoll.  The ragdoll sim (RagdollSim) only moves joint POSITIONS. To deform the skinned mesh with it we need a per-joint world transform. We build a ragdoll over the model's actual SKIN JOINTS (1:1 with the skin's joint slots, so vertex skin weights index straight into it) and, each frame, derive a per-bone rotation from the rest→current bone direction. Skinning is then the standard linear blend in SCENE space (the renderer applies importMatrix afterwards):  v' = Σ wⱼ · ( Rⱼ · (v − bindPosⱼ) + curPosⱼ )  At the bind pose Rⱼ=I and curPos=bindPos, so v'=v (the baked mesh). This needs no inverse-bind matrices because jointBindWorld·IBM = I per joint. Pure JS.


buildSkinnedRagdoll

Summary: Build a ragdoll over a model's skin joints (1:1 with skin slot indices) and a sim for it. Joint limits come from the humanoid rig where mapped, else generic topology. Returns null when the model has no skin to drive.


ragdollBoneFrames

Summary: Per-bone frames for the current sim state: rotation (3×3 row-major), current position, and bind position. Bones with no child inherit their parent's rotation (so leaf verts swing with the limb).


skinPrimitiveInterleaved

skinPrimitiveInterleaved(prim, frames, jointCount, outInterleaved)

Summary: Linear-blend skin a primitive into a standard interleaved [pos3,nrm3,uv2] buffer (UVs at o+6/o+7 are left untouched). Writes scene-space positions so the renderer's importMatrix lands them correctly. Mutates + returns `outInterleaved`.


logSkinnedRagdoll

Summary: One-time STATIC report of a built ragdoll: every joint's type + auto-detected limits, and the full constraint breakdown (bone sticks, hub bracing, ROM, signed hinges, swing cones). Prints rich console.tables when available; always returns a one-line summary string (also handed to `logFn` for the in-app console).


ragdollDebugStats

Summary: LIVE per-frame measurement: each hinge's current bend, each cone's current swing, and each ROM constraint's grandparent distance — all vs their limits, with an `ok` flag. Use it to PROVE the limits hold during simulation (and to spot violations).


assets/rig/active-body/ActiveBodyFallbackReport


assets/rig/active-body/ActiveBodyPerformanceBudgets


getActiveBodyPerformanceBudgetReport

getActiveBodyPerformanceBudgetReport(bodyCount = 0, measurements = {})


assets/rig/active-body/ActiveBodyRigBuilder

Summary: HumanoidRigBuilder — Builds a physics-ready humanoid rig (skeleton, bodies, joints, mass profile, drive profile) from entity phenotype data. Produces a StickmanRagdoll-compatible component that ActiveBodySystem can spawn as a PhysX articulation via the engine's ActiveRigController. Moved from editor/people-preview/PeoplePhenotypeRig.js into the game simulation layer so any game entity can have an active body.


buildHumanoidRig

Summary: Build a complete humanoid rig from entity data.


buildActiveBodyPayload

buildActiveBodyPayload(config, worldPosition = [0, 0, 0])

Summary: Build a spawn-ready payload for ActiveBodySystem.


assets/rig/active-body/ActiveBodySimulationTiers


buildActiveBodySimulationTierReport

buildActiveBodySimulationTierReport(bodies = [], observer = {})


assets/rig/active-body/ActiveBodySystem


assets/rig/active-body/AnimalLocomotion

Summary: engine/assets/rig/active-body/AnimalLocomotion.js — extracted from ActiveBodySystem.js (Ragdoll Stack Consolidation Roadmap, Phase 7). Owns the animal (quadruped/ arachnid) active-body path: anatomy spec generation, PBD ragdoll assembly, per-frame locomotion (gait, leg targets, tail, neural residual blending), and animal-only debug tracing. Every function here takes its dependencies as explicit parameters (ragdoll/entry/skeleton/dt/...) — none of them close over ActiveBodySystem module state, so this extraction is a pure move, not a redesign. Shared helpers (contact telemetry, balance, small math/format utils) stay in ActiveBodySystem.js and are imported back here; the reverse (the handful of names ActiveBodySystem.js still calls into, e.g. stepAnimalActiveBody) are exported from here.


createAnimalBrainProfile

createAnimalBrainProfile(skeleton, options = {}, entityId = 'animal')


animalVerticalScaleFor

animalVerticalScaleFor(skeleton, scale = 1)


animalLocalToWorldPosition

animalLocalToWorldPosition(origin, yaw, scale, local, verticalScale = scale)


createAnimalPbdRagdoll

createAnimalPbdRagdoll(skeleton, worldPosition, options = {})


stepAnimalActiveBody

stepAnimalActiveBody(entry, dt, groundY = 0, sensors = null)


logAnimalActiveBodyTrace

logAnimalActiveBodyTrace(entry, frame, groundY = 0)


formatAnimalLegDebug

formatAnimalLegDebug(entry, groundY = 0)


formatAnimalTailDebug

formatAnimalTailDebug(entry, groundY = 0)


applyAnimalPoseDrive

applyAnimalPoseDrive(entry, dt, groundY, motion)


computeAnimalLegTargets

computeAnimalLegTargets(entry, root, rootLocal, groundY, motion, basis)


applyAnimalNeuralMotor

applyAnimalNeuralMotor(entry, legTargets, tailTargets, motion, groundY, dt)


animalBodyVerticality

animalBodyVerticality(entry, groundY = 0)


animalNeuralReward

animalNeuralReward(entry, motion, currentV, targetV, coreV)


applyAnimalNeuralLegOffsets

applyAnimalNeuralLegOffsets(entry, legTargets, offsets, weight)


addAnimalNeuralOffset

addAnimalNeuralOffset(target, offsets, alias, weight, footPlanted)


computeAnimalTailTargets

computeAnimalTailTargets(entry, root, rootLocal, groundY, motion, basis, dt)


animalSupportRoot

animalSupportRoot(entry, root, groundY, motion, basis)


animalSwingLateralSweep

animalSwingLateralSweep(entry, descriptor, lift, swingAhead)


animalRestWorld

animalRestWorld(root, rootLocal, local, scale, basis, verticalScale = scale)


animalLegPhase

animalLegPhase(entry, descriptor, legCount, speedScale = null)


applyAnimalLegChainTargets

applyAnimalLegChainTargets(entry, targets, info, descriptor, root, bodyRootLocal, footTarget, lift, swing, basis)


animalLegJointPulse

animalLegJointPulse(entry, descriptor, jointIndex, chainLength, lift, swing, basis)


enforceAnimalGroundContact

enforceAnimalGroundContact(entry, groundY, dt = 1/60, sensors = null)


enforceAnimalPlantedFootContacts

enforceAnimalPlantedFootContacts(entry, groundY, dt = 1/60, sensors = null, motion = null)


applyAnimalNoIntentSettleBrake

applyAnimalNoIntentSettleBrake(entry, dt = 1/60, motion = null)


applyAnimalWrongWayBrake

applyAnimalWrongWayBrake(entry, dt = 1/60, motion = null)


computeAnimalBalance

computeAnimalBalance(entry, groundY = 0)


assets/rig/active-body/BalanceMotor

Summary: engine/assets/rig/active-body/BalanceMotor.js — extracted from ActiveBodySystem.js (Ragdoll Stack Consolidation Roadmap, Phase 7). Owns humanoid COM-based balance (Winter mass fractions) and the idle/locomotion/stumble/fallen/ getup/ko state machine that reads it. Kept as one module per the Phase 7 pre-work findings — the state machine and balance computation are tightly entangled (updateState reads balance.error/groundedFeet every frame) and splitting them would just add a needless import hop.


assets/rig/active-body/BodyAssembler

Summary: BodyAssembler — Data-driven ragdoll spec from body template + part catalog. Reads: - data/bodies/humanoid_default.json (bone hierarchy + collision rules) - data/parts/construction_volumes/*.json (per-part collision metadata) Produces a `RagdollSpec` consumable by ActiveBodySystem: { bones: [{ id, parent, partId, side, joint, collision, mass }, ...] ignorePairs: Set<"a|b"> // bone-pair keys for self-collision exclusion ignoreGroupsActive: Set<"groupA|groupB"> joints: { ball: {...}, hinge: {...}, ... } drives: { ball: {...}, hinge: {...}, ... } anatomy: { thorax, organs, collisionShells } skeletonExtensions: { mode, spineSegments, structuralBones } } This is the single source of truth for ragdoll structure. Edit the JSON files to change the body without touching code.


loadBodyTemplate

async loadBodyTemplate(url = '/data/bodies/humanoid_default.json')

Summary: Load the humanoid body template (cached after first call).


assembleBody

assembleBody(template, partCatalog, options = {})

Summary: Build a ragdoll spec from a body template + part catalog.


shouldBonesCollide

shouldBonesCollide(spec, boneA, boneB, isActive)

Summary: Test if two bones should collide given a ragdoll spec.


getFootBones

Summary: Get bone IDs that are foot contacts (for ground sensing / balance).


getBone

Summary: Get a bone by id.


assets/rig/active-body/BodyPoses

Summary: BodyPoses — Data-driven pose library + biomechanical motor controller. ══════════════════════════════════════════════════════════════════════ POSE DATA ══════════════════════════════════════════════════════════════════════ Poses live in /data/poses/*.json. Each has: id, referenceHeight, rootBone, facing, bones{name:[x,y,z]}, rotations{name:[x,y,z,w]} verticality (0..100) — used by the blend space Sequences live in /data/sequences/*.json and chain poses, though the NEW verticality system (see getPoseAtVerticality) supersedes discrete sequence-time for getup — bodies blend continuously between adjacent rungs of the verticality ladder: 0 ── 15 ── 25 ── 35 ── 45 ── 50 ── (>50 reserved) prone pushup quad  kneel half  stand   tiptoe/jump/reach kneel The ladder is based on the clinical Postural Verticality Scale (PVS) used in stroke rehab, cross-referenced with the Gross Motor Function Measure (GMFM) and standard 1D animation blend trees. ══════════════════════════════════════════════════════════════════════ MOTOR: applyKinematicBlend — FOUR STACKED LAYERS ══════════════════════════════════════════════════════════════════════ For each bone we apply, in order: 1. SOFT POSITION NUDGE  (kinematic lerp, small % per frame) Guarantees pose convergence even when forces can't. 2. VELOCITY-CLAMPED PURSUIT  (capped at muscleSpeed m/s) Close to target = slower approach (ease-in). Far = full speed. 3. MASS-SCALED PD FORCE  (the one that actually does work) Kp = m · ω² Kd = 2 · m · ω · ζ          (ζ = dampingRatio) F  = Kp·(target - pos) - Kd·velocity Mass-scaled means a 20 kg pelvis and a 0.5 kg hand have the SAME natural frequency. Like real biological muscle — body parts reach their targets on the same time scale regardless of weight. 4. MUSCLE-TONE GRAVITY COMPENSATION  (postural) Bones above knee-height get 75% of their weight canceled so posture holds without the motor doing all the lifting. Real humans use passive muscle tone; we model the same. + FORCE RISE-TIME FILTER (on the final force) Real muscles can't change force instantly. Rate-of-force- development (RFD) is 800-1500 N/s for untrained adults. Per-bone multipliers: thighs 0.7× (slow recruitment, biggest muscles), hands 1.4× (fast, small motor units). From EMG studies. + ERROR-GATED DAMPING (near target) Within 5 cm of target, velocity is damped 30% per frame. Kills steady-state jitter where the PD motor fights imperceptible position deltas. Doesn't affect in-flight motion. ══════════════════════════════════════════════════════════════════════ BIOMECHANICAL CALIBRATION  (do not "fix" these without reading!) ══════════════════════════════════════════════════════════════════════ forcePerKg = 15 × g    — untrained adult (not Olympic athlete) peak quad ≈ 15g × 8kg = 1176 N (realistic) maxForce = 1500 N      — absolute cap (big muscle groups) forceRiseRate = 1200 N/s — typical RFD. Bumping this to 3000 makes bodies feel robotic (athlete-level). omega = 6-9 rad/s      — muscle response frequency (postural 5-7, voluntary 8-12). Above 12 = twitchy. dampingRatio = 0.7     — slight overshoot (natural muscle feel). 1.0 = critical, 1.2+ = sluggish/locked. gravityTone = 0.75     — 75% of bone weight canceled via feedforward for bones above toneYThreshold. Human postural muscles do this automatically. ══════════════════════════════════════════════════════════════════════


loadPose

Summary: Load a single pose by id. Cached after first fetch. URL matches the BodyAssembler pattern (`/data/...`) since the server roots `Life/data/` as `/data/`.


loadSequence

Summary: Load a sequence and pre-load all poses referenced by its phases.


bracketWalkCyclePhase

Summary: Map gait phase (rad) to two adjacent walk-cycle poses and blend factor.


getWalkCycleWorldTargets

getWalkCycleWorldTargets(gaitPhase, measurements, anchor, yaw)


getWalkCycleWorldPoseTargets

getWalkCycleWorldPoseTargets(gaitPhase, measurements, anchor, yaw)


blendPositionTargetMaps

Summary: Linear blend of bone world positions (stand + walk-cycle additive layer).


getPoseBoneWorldPosition

getPoseBoneWorldPosition(pose, boneId, measurements, anchor, yaw)

Summary: Compute the world-space target position of a single bone for a given pose. Applies scale (measurements.height / pose.referenceHeight), yaw rotation around +Y (body facing direction), and translation to anchor.


getPoseWorldTargets

getPoseWorldTargets(pose, measurements, anchor, yaw)

Summary: Compute all target positions for a pose, keyed by bone id.


getPoseWorldPoseTargets

getPoseWorldPoseTargets(pose, measurements, anchor, yaw)


interpolatePoses

interpolatePoses(poseA, poseB, t, measurements, anchor, yaw)

Summary: Interpolate between two poses. Returns a target-position map for all bones that exist in both. Useful for smooth phase transitions within a sequence.


interpolatePoseTargets

interpolatePoseTargets(poseA, poseB, t, measurements, anchor, yaw)


applyKinematicBlend

applyKinematicBlend(ragdoll, targets, blendRate = 0.04, options = {})

Summary: Hybrid muscle-motor blend — mixes kinematic pose delivery with PD-force behavior to produce realistic active-ragdoll motion that FEELS muscular rather than teleport-y. Four layers stacked per bone: 1. SOFT POSITION NUDGE — small kinematic lerp (2-4% per frame). Guarantees pose delivery; prevents drift from constraints/collisions. 2. VELOCITY-CLAMPED PURSUIT — computes a desired velocity toward the target and caps it at a "muscle speed" limit. Far bones move at max speed; close bones slow down (ease-in on arrival). 3. PD STABILIZATION — small spring-damper toward target adds subtle inertia/settling that pure position lerp lacks. Uses applyForce so gravity/collisions/distance constraints still dominate. 4. MUSCLE-TONE GRAVITY COMP — above-ground bones get 70% gravity cancellation (like real postural muscles keeping you upright). Per-bone activation timing: big postural muscles (pelvis, thighs, chest) lead; smaller bones (hands, head) follow. Creates the "stage-by-stage effort" look real people have when getting up.


applyMotorToTargets

applyMotorToTargets(ragdoll, targets, options = {})

Summary: Apply PD (proportional-derivative) motor FORCES to drive the ragdoll toward a target pose. This is the proper active-ragdoll approach used in DeepMimic / NaturalMotion Euphoria / Karl Sims creatures: F = Kp * (target_pos - pos) - Kd * velocity Forces are applied via `solver.applyForce()` BEFORE the physics step so they get integrated as acceleration through the Verlet solver. The body rises by physically PUSHING against the ground (via collision friction on its hands/feet) — not by teleporting particles to target positions. Key properties: - Mass matters: Kp scales per bone mass so acceleration is consistent. - Kd damps overshoot and oscillation (critical damping formula below). - Gravity, collisions, and distance constraints ALL still act normally. - Momentum is preserved — no teleportation. PHASE RAMP: `phaseBoost` multiplier scales stiffness over the phase duration so the motor grows stronger as the body approaches the pose. ADAPTIVE GAIN: bones far from target get a proportional force boost — the "ramp muscle output until it works" behavior. Still applied through PD force, not position teleport. MAX FORCE CAP: prevents instability from runaway forces at large errors.


getSequenceState

Summary: Run one tick of a sequence. Returns progress info so the caller can decide when the sequence completes or what phase is active.


getCachedPose

Summary: Read a cached pose without loading. Returns null if not loaded.


getVerticalityLadder

Summary: Return a sorted ladder of {verticality, pose} for every cached pose that declares one. Sorted ascending by score.


bracketVerticality

Summary: Find the two adjacent rungs bracketing verticality `v`, plus the interpolation factor between them. Returns { lower, upper, t } where t ∈ [0,1]; t=0 means exactly lower. If v is below min or above max, clamps to end and t=0 or 1.


getPoseAtVerticality

getPoseAtVerticality(v, measurements, anchor, yaw)

Summary: Get world-space bone targets for an arbitrary verticality score. Blends between the two adjacent ladder poses, so the caller never has to know which discrete pose is "current" — just "how vertical".


getPoseTargetsAtVerticality

getPoseTargetsAtVerticality(v, measurements, anchor, yaw)


measureBodyVerticality

Summary: Measure the current body's verticality from live physics state. Uses pelvis height as fraction of the stand-pose pelvis height, mapped onto the scale so stand = 50. Below stand scales linearly; above stand (for future jumping/reaching) grows proportionally.


getCachedSequence

Summary: Read a cached sequence without loading. Returns null if not loaded.


getSequenceDuration

Summary: Compute total duration of a sequence in seconds.


Summary: Warm-cache everything we need for the active controller. Loads both prone (face-down) and supine (face-up) getup sequences so the controller can pick the right one based on the body's orientation when it decides to stand up.


Summary: Align CPG gait phase (φ=0 toe-off) with walk_cycle_q* heel-strike keys.


assets/rig/active-body/BodyRhythms

Summary: BodyRhythms — Coupled phase oscillators that generate body rhythms. ══════════════════════════════════════════════════════════════════════ WHAT THIS IS ══════════════════════════════════════════════════════════════════════ Every living body has SYNCHRONIZED internal oscillators: - HEART      ~1.0-1.5 Hz resting (60-90 bpm) - BREATH     ~0.2-0.3 Hz resting (12-18 breaths/min) - GAIT_L     ~1.0 Hz walking cadence (left leg) - GAIT_R     ~1.0 Hz antiphase to left (π offset) - ARM_L      counter-swings with RIGHT leg (inphase with GAIT_R) - ARM_R      counter-swings with LEFT leg (inphase with GAIT_L) These are the output of CENTRAL PATTERN GENERATORS (CPGs) — spinal neural circuits that don't need brain input to produce rhythm. They sync via Kuramoto-like coupling so all the oscillators lock into phase-locked relationships automatically. Engine provides the math (engine/core/math/MathOscillator.js): stepKuramotoPhases  — Euler step for coupled oscillator network phaseCoherence      — measure of synchrony (0=chaos, 1=lockstep) stepKuramotoDriven  — with external forcing (metronome, music, etc) ══════════════════════════════════════════════════════════════════════ USES IN THE ACTIVE BODY SYSTEM ══════════════════════════════════════════════════════════════════════ 1. NN OBSERVATION INPUT The NN sees sin/cos of key phases, so it knows WHERE in the gait cycle the body is. This lets it learn rhythmic actions — step forward on left foot during gait_L peak, etc. 2. POSE MODULATION (future) When intent = walk, leg targets can be modulated by gait phase to produce a stepping motion automatically. 3. VISIBLE RHYTHM (future) Chest rises with breath, head bobs with heart, body sways with gait — alive-looking NPCs even when standing still. 4. DRUG/DISEASE EFFECTS Arrhythmia = desynchronized heart. Panic = tachycardia (faster heart + breath). Paralysis = no gait coupling. Loss of coherence (order parameter drops) indicates systemic failure. ══════════════════════════════════════════════════════════════════════ COUPLING MATRIX  (who synchronizes with whom, how strongly) ══════════════════════════════════════════════════════════════════════ HEART  BREATH  GAIT_L  GAIT_R  ARM_L  ARM_R HEART     -    0.3     0       0       0      0 BREATH    0.3  -       0.1     0.1     0      0 GAIT_L    0    0.1     -      -1.0    0      1.0      (antiphase L↔R, counter with arms) GAIT_R    0    0.1     -1.0    -       1.0   0 ARM_L     0    0       0       1.0    -     -1.0 ARM_R     0    0       1.0     0      -1.0  - Positive weight = inphase pull (synchronize), negative = antiphase. Kuramoto coupling term: sin(phase_j - phase_i) × weight.


createRhythmState

Summary: Create a new rhythm state for a body. Initial phases are seeded from a per-entity RNG so different bodies aren't in lockstep (a crowd of NPCs should look like independent individuals, not a marching band).


stepRhythms

Summary: Advance the rhythm state by dt seconds using the Kuramoto model. Modulates natural frequencies based on body state: - walking intent      → gait and arms non-zero - high adrenaline     → faster heart and breath - high fatigue        → slightly elevated heart, faster breath


rhythmSample

Summary: Return sin/cos of a rhythm phase — what the NN sees. sin/cos pair is the standard representation of a circular variable for neural networks (avoids wraparound discontinuity at 2π).


shatterRhythm

shatterRhythm(state, severity = 0.5, rng = Math.random)

Summary: Desynchronize rhythms (simulate arrhythmia / panic breakdown). Adds random noise to phases and reduces coupling strength.


restoreRhythm

Summary: Re-establish healthy coupling after disruption (recovery).


assets/rig/active-body/BodySpecValidator


assets/rig/active-body/BrainProfile

Summary: engine/assets/rig/active-body/BrainProfile.js — extracted from ActiveBodySystem.js (Ragdoll Stack Consolidation Roadmap, Phase 7). Owns humanoid brain-profile creation/loading (NN tier, motor profile key resolution, lazy brain-file load) and the trust/anatomy/brain readback helpers consumed by getReadback().


createHumanoidBrainProfile

createHumanoidBrainProfile(entityId, options = {})


estimateNeuralMotorTrust

estimateNeuralMotorTrust(motor, options = {})


assets/rig/active-body/ContactTelemetry

Summary: engine/assets/rig/active-body/ContactTelemetry.js — extracted from ActiveBodySystem.js (Ragdoll Stack Consolidation Roadmap, Phase 7). Owns ground-contact resolution (Coulomb friction), per-bone/per-foot/per-sensor-anchor contact telemetry (humanoid AND animal — buildContactTelemetry dispatches to buildAnimalContactTelemetry internally so both paths share one entry point), and the contact-related debug overlay builders. Every function here takes its dependencies as explicit parameters, matching the same extraction pattern as AnimalLocomotion.js.


enforceGroundContact

enforceGroundContact(ragdoll, groundY, dt = 1/60, sensors = null)


buildContactTelemetry

buildContactTelemetry(entry, groundY = 0, sensors = null)


buildAnimalContactTelemetry

buildAnimalContactTelemetry(entry, groundY = 0, sensors = null)


buildBoneContactRecord

buildBoneContactRecord(entry, spec, p, groundY, sensors)


buildSensorAnchorTelemetry

buildSensorAnchorTelemetry(entry, groundY = 0, sensors = null)


buildSensorAnchorRecord

buildSensorAnchorRecord(entry, anchor, groundY, sensors)


approximateSensorAnchorPoint

approximateSensorAnchorPoint(entry, anchor, driver, reference = null)


makeSensorAnchorBasis

makeSensorAnchorBasis(entry, anchor, driver, reference = null)


assets/rig/active-body/DebugOverlay

Summary: engine/assets/rig/active-body/DebugOverlay.js — extracted from ActiveBodySystem.js (Ragdoll Stack Consolidation Roadmap, Phase 7). Owns pure display/readback helpers: overlay toggle resolution and the make*Overlay builders consumed by ActiveBodySystem.getDebugOverlay()/getReadback(). Deliberately excludes buildMotorAuthorityMap — despite being overlay-adjacent, it's also consumed by live simulation (applyFootReflexTarget/applyBraceProtection Targets), so it stays with the reflex/motor-authority code, not here.


makeOverlayToggleMap

makeOverlayToggleMap(source = null, fallback = DEFAULT_ACTIVE_BODY_OVERLAY_TOGGLES)


Summary: engine/assets/rig/active-body/DirectionDiagnostics.js — extracted from ActiveBodySystem.js (Ragdoll Stack Consolidation Roadmap, Phase 7). Owns per-frame pelvis-displacement direction tracking (updateDirectionMotion) and the direction/facing diagnostics readback consumed by getDirectionDiagnostics()/getReadback() and the orientation-audit debug logging (still in ActiveBodySystem.js).


assets/rig/active-body/MotionPriorTraining


loadAniMotionPrior

async loadAniMotionPrior(state, options = {})


mixAniMotionTeacher

mixAniMotionTeacher(teacher, state, entry, ragdoll, bonesByName, intent = null, options = {})


assets/rig/active-body/NeuralMotor

Summary: NeuralMotor — per-entity self-training MLP for active-ragdoll control. ══════════════════════════════════════════════════════════════════════ WHAT THIS IS (read first) ══════════════════════════════════════════════════════════════════════ The NN adds position RESIDUAL offsets (±8cm per bone) on top of the kinematic verticality pose targets. It is NOT a force generator. The force that actually lifts the body comes from the PD motor in BodyPoses.js, which uses the (NN-refined) targets. This is called "residual policy learning" — same approach used by DReCon (Ubisoft, 2020) and DeepMimic. The NN learns the RESIDUAL between hand-authored poses and what the body actually needs. targets_kinematic  (from verticality blend) + NN_residual  (from this file, trained online) = targets_final → PD motor tracks targets_final with realistic muscle force ══════════════════════════════════════════════════════════════════════ TRAINING ALGORITHM: AWR (Advantage-Weighted Regression) ══════════════════════════════════════════════════════════════════════ Every frame: 1. Compute REWARD from body state (pose match, upright, stable, goal progress — see computeReward). 2. Maintain an EMA baseline of reward. 3. Advantage = reward - baseline. 4. Gradient weight = clamp(exp(β·advantage), 0.25, 3). 5. Teacher = what the kinematic controller would nudge this frame. 6. Loss = awrWeight × MSE(NN_output, teacher). 7. One SGD step per frame. Effect: NN imitates the teacher MORE in above-average states and LESS in below-average. This turns behavior cloning into a proper RL method. We don't need full PPO with rollout buffers — training is online and stable because the teacher is always available. STABILITY GATE: training is skipped when the body is thrashing (core velocity > 24 m/s or head below pelvis). Chaotic frames produce noise teacher signals that would corrupt the NN. ══════════════════════════════════════════════════════════════════════ BRAIN TIERS ══════════════════════════════════════════════════════════════════════ tiny   (default NPCs):  43→64→64→60             ≈ 41 KB small  (named NPCs):    43→128→128→60           ≈ 130 KB medium (player default):43→256→256→256→60       ≈ 1.2 MB large  (player upgrade):43→512→1024→1024→60     ≈ 6.6 MB huge   (endgame cap):   43→768→1024³→60         ≈ 10 MB (Input dim 43 = 36 body state + 4 intent one-hot + 3 goal vector.) 10 MB hard cap per brain (enforced at serialize-time). Upgrades via Net2Net weight transplant (upgradeBrain) — preserves learned behavior, new neurons start near-zero and learn later. ══════════════════════════════════════════════════════════════════════ DETERMINISM ══════════════════════════════════════════════════════════════════════ Per-entity seeded PRNG (engine's mulberry32 + FNV-1a string hash of entity id). Same id always produces the same initial brain. Different ids (player, npc_parent, thief_jin) diverge immediately into unique "personalities". Drug/seizure/tremor effects also use the per-entity RNG, so effects are reproducible per body. ══════════════════════════════════════════════════════════════════════ NEUROMODIFIERS (drugs, disease, damage) ══════════════════════════════════════════════════════════════════════ The forward pass accepts five perturbations applied to hidden-layer activations, grounded in real pharmacology: noise      — Gaussian σ (alcohol → loss of coordination) dropout    — random activation kill (seizure / KO) scale      — output multiplier (paralytic=0, stimulant=1.4) tremor     — coherent sinusoidal (Parkinsonian 4-6 Hz) delay      — observation buffer lag (concussion) seizureAmp — random spikes in activations See NeuroModifiers.js for preset profiles. ══════════════════════════════════════════════════════════════════════ No external ML library — pure JS, uses engine/core/math/MathRandom.js for shared PRNG utilities. Runs on CPU at 60 Hz for ~20 bodies on a laptop. Training cost ~0.3ms per body per frame (tiny tier).


tierFromIntelligence

tierFromIntelligence(score, magical = false)

Summary: Map an intelligence score to the appropriate tier name. Magical tiers (IQ > 100) only return when `magical=true` — otherwise the character is capped at the highest mortal tier (huge).


nextTier

Summary: Get the next tier above the current one, or null if already at top.


createNeuralMotor

createNeuralMotor(tier = DEFAULT_TIER, seedSource = null)

Summary: Create a NeuralMotor. If `seedSource` is given (typically the entity id string), initialization uses a deterministic PRNG — the same id always produces the same starting brain. Different ids get different brains (different "personalities") via string hashing.


buildObservation

buildObservation(ragdoll, bonesByName, currentV, targetV, intent = null, rhythms = null, sensors = null)

Summary: Build the observation vector. Dimensions [21..23] originally held a static gravity vector placeholder — we've REPURPOSED these for CPG RHYTHM signals, which give the NN an internal time-base for rhythmic motor actions (walking, breathing): [0..2]   pelvis position (x,y,z) [3..5]   pelvis velocity [6..8]   chest - pelvis (spine vector) [9..11]  shoulder axis (rShoulder - lShoulder) [12..14] COM - pelvis [15]     verticality (0..1, normalized to /50) [16]     verticality gap (target - current, normalized) [17..20] foot contacts (lFoot y, rFoot y, lFoot vy, rFoot vy) [21]     sin(gait_L phase)  ← CPG: left-leg step cycle [22]     cos(gait_L phase) [23]     sin(breath phase)  ← CPG: breathing cycle [24..35] left/right hand/knee positions relative to pelvis [36..39] intent one-hot (stand/walk/duck/act) [40]     target verticality / 50 (goal normalized) [41..42] target direction (xz, world-space, for walk/act) [43..46] terrain (height below / ahead / slope / lateral) — see OBS_SIZE comment [47..50] social (nearest-agent rel xz + rel vxz) [51..54] high-level motor intent (guard/brace/attack/recover) [55..77] tactile/proprioception block The sin/cos encoding of phase is standard NN representation for circular variables (avoids wraparound discontinuity at 2π).


neuralMotorForward

neuralMotorForward(motor, obs, time = 0, options = null)

Summary: Forward pass through the network with neuromodifier effects applied. Returns per-bone Δxyz offsets (in ragdoll name order).


getNeuralInferenceBackendReport

getNeuralInferenceBackendReport(bodyCount = 0, gpu = null)


computeReward

computeReward(ragdoll, bonesByName, poseTargets, intent)

Summary: Compute a scalar reward for the current body state. Real DeepMimic-style reward function — additive terms with sensible weights. All terms ∈ [0, 1] so total ≤ sum of weights (~5). poseMatch     — mean bone distance to pose target (closer = higher) upright       — head Y above pelvis Y, normalized (no face-plant) stable        — low velocity = higher (not thrashing) goalProgress  — state-dependent (verticality match, walk speed, etc) Returns: reward ∈ [0, ~5] approximately; scale to [0, 1] with r/5 for AWR weight


trainNeuralMotor

trainNeuralMotor(motor, obs, teacherOffsets, reward = null, opts = {})

Summary: Advantage-Weighted Regression (AWR) training step. Standard behavior cloning: minimize MSE(NN_out, teacher) AWR: minimize reward_weight * MSE(NN_out, teacher) Effect: NN imitates the teacher ONLY when the state is good (high reward). Bad states contribute ~0 gradient. This implicitly learns which actions lead to high reward — a form of reinforcement learning without full PPO machinery. Used in shipping games (DReCon) and research (AWR, RWR).


applyNeuralOffsets

Summary: Apply motor output as per-bone target offsets. Call AFTER the kinematic blend has computed its targets; the NN adds corrective deltas.


computeTeacherSignal

computeTeacherSignal(kinematicTargets, ragdoll, bonesByName, rate = 0.15)

Summary: Compute the "teacher signal" — the delta the kinematic controller would apply this frame. For DAgger training, this is what the NN tries to predict. Simple formula: (kinematic_target - actual_position) × rate.


serializeMotor

Summary: Serialize a NeuralMotor to a compact JSON-safe object. Weights are packed as base64-encoded Float32 bytes. Enforces 10 MB per-brain cap. Layout: { magic, version, tier, hiddens[], bytes (base64), framesTrained, lossEMA }


deserializeMotor

deserializeMotor(motor, data, options = {})

Summary: Load serialized brain data into an existing motor (replaces weights). Rebuilds layer chain if hiddens differ. Returns true on success.


analyzeMotorStorage

analyzeMotorStorage(motor, packedBytes = null)


saveBrain

Summary: Save a brain under `id`. Enforces 10 MB per-brain cap.


loadBrain

Summary: Load a brain by id. Returns serialized data or null.


deleteBrain

Summary: Delete a brain.


listBrains

Summary: List all saved brain ids with their sizes.


exportBrainAsJson

Summary: Export a brain as a downloadable JSON file (base64 inside). Useful for sharing trained brains between players.


importBrainFromJson

Summary: Import a JSON string (from exportBrainAsJson) into a motor.


upgradeBrain

Summary: Upgrade a motor to a bigger tier. Weight matrices are transplanted so the pre-upgrade behavior is preserved; new neurons start with near-zero influence and gradually learn new behaviors. Returns true if upgraded, false if target tier isn't actually bigger.


getUpgradeOptions

Summary: Check which tiers an entity can upgrade to (tier order: tiny→small→medium→large→huge).


Summary: engine/assets/rig/active-body/NeuralResidualController.js — extracted from ActiveBodySystem.js (Ragdoll Stack Consolidation Roadmap, Phase 7). Owns the humanoid rotation-residual cluster: converting NN bone offsets into axis-angle residuals blended on top of kinematic rotation targets, and zeroing out idle-state NN drift on the horizontal (and, for a fixed set of upper-body bones, vertical) axes so a "resting" NN doesn't slowly walk the pose off-target. Pure functions — callers (the pose-target pipeline, still in ActiveBodySystem.js) own all the surrounding per-frame state.


computeNeuralRotationResidualHints

computeNeuralRotationResidualHints(rotationTargets, poseState, neuralOffsets = null, weight = 1)


neuralOffsetToAxisAngle

neuralOffsetToAxisAngle(neuralOffsets, boneId, weight = 1)


assets/rig/active-body/NeuroModifiers

Summary: NeuroModifiers — preset effect profiles that corrupt the NeuralMotor's forward pass to produce realistic neurological symptoms. Each profile mutates the `motor.modifiers` bag on a NeuralMotor instance. Multiple effects stack by summing noise/dropout and multiplying scale. Grounded in real pharmacology/neurology: - alcohol:   inhibits GABA → activation noise + slow reaction (delay) - paralytic: blocks ACh at NMJ → output scale → 0 (limp) - stimulant: excess catecholamines → amplified output, jitter - tranq:    dopamine block → output scale 0.4, delay 3 - poison:   neural death → growing dropout + decreasing scale - seizure:  hypersynchronous discharge → huge activation spikes - tremor:   basal-ganglia dysfn (Parkinson) → coherent 4-6 Hz oscillation


applyNeuroProfile

Summary: Apply a preset profile to a NeuralMotor (replaces current modifiers).


rampNeuroProfile

Summary: Blend toward a profile over time — e.g. poison progression. `t` ∈ [0,1] is how far along the progression is.


clearNeuroModifiers

Summary: Reset all neuromodifiers to healthy.


applyNeuroJerks

applyNeuroJerks(ragdoll, motor, time, dt)

Summary: Apply neuromodifier effects DIRECTLY to the ragdoll bones, bypassing the NN. Needed because the NN only runs during `getup` state; seizures etc. should be visible in ANY state (idle, fallen, walking). This is the physical manifestation of motor-cortex corruption: tremor      → coherent sinusoidal force (Parkinsonian shake) seizureAmp  → random large impulses (tonic-clonic thrashing) noise       → Gaussian jitter on force (alcohol wobble) scale       → global force multiplier (paralytic = 0 → body goes limp) Scales with bone mass so effects feel right (heavy pelvis thrashes harder than hand). Called each frame in the main update loop.


assets/rig/active-body/OrientationAudit

Summary: engine/assets/rig/active-body/OrientationAudit.js — extracted from ActiveBodySystem.js (Ragdoll Stack Consolidation Roadmap, Phase 7). Owns pure console.log debug tracing: per-frame locomotion-target deltas, the body-vs-particle-vs-facing orientation audit, and the authored-pose directional-consistency self-check (knees/elbows/toes/belly orientation). Distinct from DebugOverlay.js's structured readback data — this is developer-console tracing only, no return values consumed by callers.


logLocomotionTargetTrace

logLocomotionTargetTrace(entry, ragdoll, targets, meta)


assets/rig/active-body/PhysicsMotor

Summary: engine/assets/rig/active-body/PhysicsMotor.js — extracted from ActiveBodySystem.js (Ragdoll Stack Consolidation Roadmap, Phase 7). Owns the pure physics-application layer for the humanoid Verlet/PBD ragdoll: initial walking-skeleton kinematic pose construction, pelvis anchoring, per-bone joint-torque drive, and hinge-limit computation/enforcement + compliance scaling. Separate concern from pose-TARGET computation (still in ActiveBodySystem.js) — this module only turns targets/limits into forces applied to the ragdoll's particles.


setRagdollPose

setRagdollPose(ragdoll, anchor, m, options = {})


applyJointTorques

applyJointTorques(ragdoll, skeleton, dt, state, driveByBone = null)


computeHingeLimits

computeHingeLimits(ragdoll, m, jointLimitsByBone = null)


complianceStrengthFor

complianceStrengthFor(jointLimitsByBone, names, fallback = 1)


assets/rig/active-body/PoseAuthority

Summary: engine/assets/rig/active-body/PoseAuthority.js — extracted from ActiveBodySystem.js (Ragdoll Stack Consolidation Roadmap, Phase 7). This is the hardest and last Phase 7 cluster: the humanoid standing/getup pose-target pipeline — applyStandingController (kinematic pose blending, walk-cycle overlay, balance correction, rhythm/breathing modulation, contact reflexes, brace protection, NN residual blending, training) and applyGetupController (orientation-aware rise sequencing), plus every helper they call (limb/shoulder/head stabilization, reflex classification, motor-authority mapping, pose-state resolution). Deliberately NOT split further — these helpers are tightly entangled with the two controller functions and with each other; see the Phase 7 pre-work notes in the roadmap doc.


coupleShoulderGirdleTargets

coupleShoulderGirdleTargets(entry, ragdoll, targets, yaw)


getBodySideAxis

getBodySideAxis(entry, ragdoll, yaw = null, preferYaw = false)


clampTargetToSide

clampTargetToSide(targets, name, base, side, minSide, isLeft, strength = 1)


alignFootToeTargets

alignFootToeTargets(entry, targets, yaw, leftLegIsSwing = false, rightLegIsSwing = false, isWalking = false)


clampLimbSideTargets

clampLimbSideTargets(entry, ragdoll, targets, yaw = null)


dampRelativeVelocity

dampRelativeVelocity(child, parent, keep, dt)


applyBalanceCorrectionToTargets

applyBalanceCorrectionToTargets(targets, balanceState, weight = 1)


applyRhythmPoseModulation

applyRhythmPoseModulation(targets, rhythms, weight = 1, fatigue = 0)


applyContactReflexTargets

applyContactReflexTargets(entry, ragdoll, targets, options = {})


reflexConfidence

reflexConfidence(contactTelemetry, mode, leftTrust, rightTrust)


applyFootReflexTarget

applyFootReflexTarget(entry, targets, record, options)


Summary: engine/assets/rig/active-body/UniversalLegScheduler.js — extracted from ActiveBodySystem.js (Ragdoll Stack Consolidation Roadmap, Phase 7). Owns N-legged support-polygon analysis and gait-preset recommendation for the generic/readback-only "universal leg scheduler" — the leg discovery, phase, and support-polygon math shared conceptually with the animal gait system, but exposed here purely as debug/readback (ActiveBodySystem's getReadback() is the only call site; it does not drive live pose targets).


normalizeUniversalLegDescriptors

normalizeUniversalLegDescriptors(descriptors, entry, allowMetadataOnly = true)


assets/rig/active-body/anatomy/BioelectricSignals


stepBioelectricSignals

stepBioelectricSignals(state, inputs = {}, dt = 0)


assets/rig/active-body/anatomy/BreathingBodyCoupling


getBreathingBodyCouplingReadback

getBreathingBodyCouplingReadback(diaphragm = null, rhythms = null)


assets/rig/active-body/anatomy/CirculatorySystem


stepCirculation

stepCirculation(state, inputs = {}, dt = 0)


assets/rig/active-body/anatomy/DiaphragmSystem


stepDiaphragm

stepDiaphragm(state, rhythms = null, dt = 0)


assets/rig/active-body/anatomy/NervousSystem


stepNervousSystem

stepNervousSystem(state, inputs = {}, dt = 0)


assets/rig/active-body/anatomy/OrganDamageSystem


applyOrganDamage

applyOrganDamage(state, organs, target, amount, options = {})


stepOrganDamage

stepOrganDamage(state, organs, inputs = {}, dt = 0)


assets/rig/active-body/anatomy/OrganSystem


createOrganState

createOrganState(anatomy = null, attachments = null)


assets/rig/active-body/anatomy/PBPKSystem


addPBPKDose

addPBPKDose(state, substanceId, amount, options = {})


assets/rig/active-body/anatomy/SoftTissueSystem


stepSoftTissue

stepSoftTissue(state, inputs = {}, dt = 0)


assets/rig/active-body/anatomy/VitalSigns


stepVitalSigns

stepVitalSigns(state, inputs = {}, dt = 0)


assets/rig/active-body/anatomy/index

Summary: engine/assets/rig/active-body/anatomy/index.js — anatomy subsystem barrel. Every other subsystem in the engine exposes a barrel (`voxel/index.js`, `sim/ai/index.js`, `gameplay/perception/index.js`); the anatomy modules did not, which is part of why they were unreachable from any compiled bundle. This is purely additive — no module below is modified, and nothing that already imports these files by path is affected. The subsystem models a body as interacting physiological systems rather than a hit-point total: - `OrganSystem`        per-organ integrity, perfusion, oxygenation, function - `OrganDamageSystem`  wounds by mechanism, bleeding, hypoxia, shock - `CirculatorySystem`  cardiac output, per-limb and per-organ blood flow - `NervousSystem`      spinal cord, 16 peripheral regions, motor authority, and per-region motor/sensory/touch/pain channels - `VitalSigns`         heart rate, respiration, blood pressure, SpO2 - `DiaphragmSystem`    breathing drive and rib/sternum coupling - `BreathingBodyCoupling` breathing translated into bone pose offsets - `SoftTissueSystem`   mass-spring tissue displacement and strain - `PBPKSystem`         multi-compartment pharmacokinetics for any substance - `BioelectricSignals` low-level signal propagation


assets/rig/index

Summary: Stable public rig surface for raw modules, Engine.Rig, and bundled PE.Rig. The named groups preserve each source module's boundary while the flat functions keep the existing engine/assets API available without adapters.


assets/vehicle/ChassisDetector

Summary: engine/assets/vehicle/ChassisDetector.js — identify the chassis + body parts (spec §11). The chassis is the largest non-wheel mesh; remaining meshes are categorized by name (glass/lights/doors) so segmentation and material edits (tint glass, emissive headlights, detach doors) can target them.


detectChassis

Summary: Identify chassis + body part nodes, excluding the given wheel node ids.


assets/vehicle/VehicleMath

Summary: engine/assets/vehicle/VehicleMath.js — minimal column-major 4x4 + quaternion helpers for vehicle rig assembly and the visual wheel transform chain. Kept local and dependency-free so the vehicle layer is self-contained and the math stays deterministic (and gate-testable without the renderer).


multiply

Summary: Column-major multiply: out = a * b.


composeTRS

Summary: Compose a TRS matrix (scale may be a number or [x,y,z]).


quatFromAxisAngle

Summary: Quaternion (xyzw) from an axis + angle (radians).


quatMul

Summary: Hamilton product a*b (xyzw).


transformPoint

Summary: Transform a point (vec3) by a column-major 4x4.


nodeLocalMatrix

Summary: Local transform of a node (matrix override or TRS).


computeWorldMatrices

Summary: World matrices for every node, accumulating the parent chain. Returns a Map(nodeId → Float32Array(16)).


assets/vehicle/VehicleRigBuilder

Summary: engine/assets/vehicle/VehicleRigBuilder.js — assemble a VehicleAsset (spec §11).  Vehicles are rigged, not merely loaded. This turns detected wheels + chassis into explicit runtime data: WheelSlots (with suspension/tire/drivetrain), front-axle steering, a compound-collider descriptor for the chassis (built via the engine's ConvexDecomposition on demand), and a confidence. Detection failures (e.g. wheels merged into the body) flag needsCorrection rather than guessing — the editor exposes every decision (rule 62).


buildVehicleRig

Summary: Build a vehicle rig for a model and attach it at model.rigs.vehicle.


assets/vehicle/WheelDetector

Summary: engine/assets/vehicle/WheelDetector.js — find wheels (spec §11).  Detection chain (cheap → expensive), each a HINT with a confidence, never an assertion: name match → cylinder geometry → symmetry / low-corner position → (editor correction). Wheels that are merged into the body mesh won't be found here and fall through to the editor (see VehicleRigBuilder.needsCorrection).


detectWheels

Summary: Detect wheel candidates in a model.


assets/vehicle/WheelPhysics

Summary: engine/assets/vehicle/WheelPhysics.js — suspension, tire slip, and the visual wheel transform chain (spec §11). Deterministic CPU model used for the inspector/demo and gates; production deployments route the same WheelSlot data into the engine's GPUVehicle. Wheel visuals FOLLOW physics state: chassisWorld · socket · suspension · steer · spin · meshCorrection.


suspensionForce

suspensionForce({ restLength, currentLength, stiffness, damping, compressionVelocity = 0 })

Summary: Suspension spring + damper force. compression   = restLength - currentLength springForce   = stiffness * compression damperForce   = damping  * compressionVelocity normalLoad    = max(0, springForce + damperForce)


tireForce

tireForce(slip, normalLoad, grip, slipStiffness = 12)

Summary: A slip-based tire force (Pacejka-lite): linear near zero slip, saturating at the friction limit `normalLoad * grip`. `slipStiffness` shapes the knee.


longitudinalSlip

Summary: Longitudinal slip ratio from wheel surface speed vs ground speed.


freeRollAngularVelocity

Summary: Free-rolling angular velocity (rad/s) for a given ground speed.


visualWheelMatrix

visualWheelMatrix(chassisWorld, slot, state = {})

Summary: Compose the visual world matrix for a wheel from chassis world + slot + state.


advanceSpin

advanceSpin(spinAngle, groundSpeed, radius, dt)

Summary: Advance a wheel's spin angle from a ground speed (visual integration).


assets/vehicle/WheelSlot

Summary: engine/assets/vehicle/WheelSlot.js — vehicle rig schemas (spec §11).  A vehicle is NOT a mesh: it's a visual model + chassis collider + wheel slots + suspension + tire model + drivetrain. Wheel runtime data is explicit and separate from the visible wheel mesh (matching Jolt/Unity wheel colliders), so visuals follow physics rather than the other way around.


createWheelSlot

Summary: A wheel slot: the runtime data for one wheel, decoupled from its visual mesh.


createVehicleAsset

Summary: The assembled vehicle rig produced by VehicleRigBuilder. It references the model's nodes/meshes and carries explicit runtime data; it can drive the engine's GPUVehicle physics or the CPU WheelPhysics fallback.


assets/vehicle/WheelSpinVisual

Summary: engine/assets/vehicle/WheelSpinVisual.js — visual wheel-spin transforms for a rigged vehicle. Separated from physics (WheelPhysics) and detection so the importer/viewer, the editor preview, and the runtime can all reuse one correct implementation.  Approach (matches Unity `transform.Rotate(..,Space.Self)` / Godot `rotate_object_local`): the spin is built in the wheel's MESH-LOCAL space from the detector's per-wheel hub (`localPivot`) and axle (`axleAxisLocal`), then the node hierarchy + import correction carry it to the right world position and orientation automatically (the renderer does `draw = baseDraw · spin_local`).  This avoids transforming the axle into world space, PCA axis ambiguity on low-poly wheels, and pivot guessing — the failure modes of a world-space spin. The only world-space step is a per-wheel SIGN so mirrored wheels roll the same way; the axle vector itself stays the exact per-wheel local axis.


rotAxis

Summary: Column-major rotation about an arbitrary (unit-normalized) axis.


pivotSpin

Summary: Spin about an axis through a pivot: T(pivot) · R(axis,angle) · T(-pivot).


buildWheelSpinMap

Summary: Build a per-wheel spin descriptor map for a rigged model. Each entry carries the wheel's MESH-LOCAL pivot + axle (so the renderer applies the spin in local space: `draw = baseDraw · pivotSpin(pivot, axle, angle)`). The local axle is pre-signed so every wheel rolls the same visual direction. Steerable wheels also carry their local steer axis so the renderer can yaw them with input.


rollAngle

Summary: Spin angle (radians) for a wheel that has rolled `distance` metres.


assets/vpath

Summary: engine/assets/vpath.js — virtual path system.  Users never manage raw filesystem paths. Everything addresses through a scheme://path virtual path. The resolver (AssetResolver) maps a virtual path to a concrete backend handle; this module only parses/normalizes/builds them.  asset://   project asset library user://    user-selected local folder (File System Access) cache://   OPFS / local engine cache cloud://   optional remote mirror temp://    transient drag-drop import surface:// live render surface (no backing file)


parseVPath

Summary: Parse a virtual path into { scheme, path, segments }.


makeVPath

Summary: Build a virtual path from a scheme + path parts.


normalizeSegments

Summary: Collapse duplicate/empty separators and strip leading/trailing slashes.


withScheme

Summary: Swap the scheme of a virtual path, keeping the same path (e.g. for backups).


vpathBasename

Summary: The basename (last segment) and extension of a virtual path.


cachePathFor

Summary: Standard cache path for a converted artifact, keyed by content hash.


assets/weapon/WeaponRig

Summary: engine/assets/weapon/WeaponRig.js — canonical weapon socket/part schema (spec §13). A weapon is a model + sockets (muzzle/chamber/grips/sight/…) + moving parts (slide/bolt/hammer/…) + a fire/reload timeline + a state machine. This is a GAME ABSTRACTION: sockets/timings are gameplay anchors, not real-firearm data. Every detection is a hint with confidence; ambiguous nodes go to the editor.


createWeaponRig

Summary: Create a weapon rig descriptor.


assets/weapon/WeaponRigBuilder

Summary: engine/assets/weapon/WeaponRigBuilder.js — assemble a WeaponRig from a model: detect sockets + moving parts + forward/muzzle, attach default fire/reload timelines and a state machine seed, and flag needsCorrection when required sockets are missing. Attaches at model.rigs.weapon. GAME ABSTRACTION ONLY.


buildWeaponRig

Summary: Build a weapon rig and attach it at model.rigs.weapon.


assets/weapon/WeaponSocketDetector

Summary: engine/assets/weapon/WeaponSocketDetector.js — find weapon sockets + moving parts by NAME (priority-ordered patterns) and resolve the forward/aim axis + muzzle by geometry (a firearm is long along the barrel; the muzzle is the front-most node along that axis). Hints + confidence; ambiguous → editor. GAME ABSTRACTION ONLY — no real-firearm dimensions or ballistics here.


detectWeapon

Summary: Detect weapon sockets, moving parts, forward axis, and muzzle.


assets/weapon/WeaponStateMachine

Summary: engine/assets/weapon/WeaponStateMachine.js — a small, deterministic weapon state machine: idle ⇄ firing ⇄ empty, with reloading. It models a magazine + a chambered round (total = ammo + chambered) and auto-cycles a fresh round after each shot, so dry-fire, last-round, and reload-from-empty all behave correctly. GAME ABSTRACTION ONLY — no real-firearm mechanism is modelled.


assets/weapon/WeaponTimeline

Summary: engine/assets/weapon/WeaponTimeline.js — keyframed event tracks for weapon actions (fire, reload). An action is an ordered list of { t, type, payload } events over a duration; `eventsBetween` returns events crossed in a time step so the host fires them exactly once (deterministic, frame-rate independent). GAME ABSTRACTION — timings are gameplay anchors, not real mechanisms.


createWeaponAction

Summary: Create a single action timeline (e.g. one shot, or a reload).


defaultFireAction

Summary: A default semi-auto fire action (normalized 0..duration seconds).


defaultReloadAction

Summary: A default reload action.


eventsBetween

Summary: Events whose time t lies in (t0, t1]. Use with an advancing playhead so each event fires exactly once regardless of frame rate.


createTimelinePlayer

Summary: A small play-once cursor over an action.


audio/SpatialAudioEnvironment

Summary: SpatialAudioEnvironment.js - Advanced Spatial Audio Processing Industry-grade environment audio: material absorption, ground reflection, environment zones (underwater/cave/outdoor), diffraction, and dynamic reverb. Research basis: - FMOD/Wwise material absorption coefficients (ISO 354 standard) - Image-source method for early reflections (Allen & Berkley 1979) - Underwater acoustics: 300Hz LPF + extended reverb (Wikipedia/Navy research) - Diffraction: UTD edge diffraction approximation (Kouyoumjian & Pathak 1974) - Ground reflection: single-bounce mirror source below floor plane All processing uses Web Audio API nodes (BiquadFilter, Gain, Delay, Convolver). Zero external dependencies.


createSpatialEnvironment

createSpatialEnvironment(ctx, config = {})

Summary: Create the spatial audio environment system. Attaches to an existing AudioContext and provides environment processing.


setEnvironmentRaycast

Summary: Set the raycast function for environment probing. Function signature: (origin: [x,y,z], direction: [x,y,z], maxDist: number) => { hit, distance, position, normal, entityId } | null


setEntityMaterialLookup

Summary: Set the entity material lookup function. Function signature: (entityId) => 'stone' | 'wood' | 'metal' | etc.


setWaterSurfaceY

Summary: Set the water surface Y level. Listener below this = underwater. Pass null to disable underwater detection.


updateEnvironment

Summary: Update the environment system each frame. Detects environment zone, updates ground reflection, handles underwater.


computeOcclusion

Summary: Compute material-aware occlusion between a source and the listener. Returns { factor: 0-1, lpfMult: 0-1 } where factor affects volume, lpfMult affects high-frequency content (material filtering).


computeDiffraction

Summary: Compute diffraction factor for partially occluded sources. Casts multiple rays slightly offset from the direct path. If some rays get through, the sound is partially diffracted (not fully blocked).


computeFullOcclusion

Summary: Compute full spatial occlusion for a sound source. Combines material-based occlusion with edge diffraction. Returns 0-1 where 0 = fully blocked, 1 = clear.


getEnvironmentIR

Summary: Generate a procedural impulse response for a given environment. Cached per environment to avoid regeneration.


connectToBridge

Summary: Connect the environment system's master filters into the audio bridge's output chain. Call once after both bridge and environment are created. Inserts: bridge.masterGain → env.masterHPF → env.masterLPF → ctx.destination Also connects ground reflection send from bridge.compressor.


getMaterialAbsorption

Summary: Get absorption data for a material key.


getEnvironmentPresets

Summary: Get all available environment presets.


forceEnvironmentZone

Summary: Force a specific environment zone (overrides auto-detection). Pass null to re-enable auto-detection.


getEnvironmentStats

Summary: Get current environment stats.


destroySpatialEnvironment

Summary: Destroy the environment system and release all audio nodes.


audio/bridge/CharacterAudioBridge

Summary: CharacterAudioBridge.js - Character Movement → Audio Events Triggers footsteps, jumps, lands, and body foley based on character movement state (velocity, grounded, etc).


createCharacterAudioBridge

Summary: Create a character movement audio bridge.


tickCharacterAudio

tickCharacterAudio(bridge, audioEngine, entityId, state, dt)

Summary: Tick the character audio bridge for a single character. Call once per frame per character entity.


removeCharacter

Summary: Clean up character state when entity is destroyed.


destroyCharacterAudioBridge

Summary: Destroy the character audio bridge and clean up all state.


audio/bridge/DestructionAudioBridge

Summary: DestructionAudioBridge.js - Destruction Events → Audio Hooks into VoronoiFracture, UnifiedDamageSystem, SeismicSystem, and ShockPropagation to trigger shattering, crumbling, rumble, and explosion sounds.


createDestructionAudioBridge

Summary: Create a destruction audio bridge.


processDamageAudioEvent

Summary: Process a damage event.


processFractureAudioEvent

Summary: Process a fracture event (Voronoi fracture).


processSeismicAudioEvent

Summary: Process a seismic event (earthquake rumble).


flushDestructionAudio

Summary: Flush pending destruction audio events.


setDestructionAudioListenerPos

Summary: Update listener position.


audio/bridge/EmitterAmbientBridge

Summary: EmitterAmbientBridge.js - Active Emitters → Continuous Ambient Loops When a substance emitter is active and has an ambientLoop defined, this bridge manages a looping spatial audio source at the emitter position. Sources are created when emitters start and removed when they stop.


createEmitterAmbientBridge

Summary: Create an emitter ambient bridge.


syncEmitterAmbients

Summary: Sync active emitters with ambient audio loops. Call each frame with the current list of active emitters.


registerEmitterLoop

registerEmitterLoop(bridge, emitterId, sourceId, substanceId, position)

Summary: Register that a loop was started (store sourceId).


stopAllEmitterAmbients

Summary: Stop all active ambient loops.


audio/bridge/FluidAudioBridge

Summary: FluidAudioBridge.js - Fluid Events → Audio Hooks into fluid simulation (splashes, phase changes) and triggers appropriate audio events.


createFluidAudioBridge

Summary: Create a fluid audio bridge.


processFluidSplash

processFluidSplash(bridge, position, magnitude, currentTime)

Summary: Process a fluid splash event.


processPhaseChangeAudio

processPhaseChangeAudio(bridge, phaseType, position, magnitude, currentTime)

Summary: Process a phase change event.


flushFluidAudio

Summary: Flush pending fluid audio events.


setFluidAudioListenerPos

Summary: Update listener position.


audio/bridge/PhysicsAudioBridge

Summary: PhysicsAudioBridge.js - Physics Collision → Audio Events Hooks into physics contact callbacks (PhysX/PBD) and triggers material-aware impact sounds via the AudioEngine.


createPhysicsAudioBridge

Summary: Create a physics audio bridge.


processPhysicsContact

Summary: Process a physics contact event.


flushPhysicsAudio

Summary: Flush pending physics audio events.


setPhysicsAudioListenerPos

Summary: Update listener position.


audio/bridge/SpellAudioBridge

Summary: SpellAudioBridge.js - Spell Cast → Audio Events Resolves soundHint strings from SpellGenerator and SpellElementMixer into AudioEngine trigger calls.


createSpellAudioBridge

Summary: Create a spell audio bridge.


buildSpellAudioTrigger

buildSpellAudioTrigger(bridge, spell, position, currentTime)

Summary: Build audio trigger config from a spell cast.


buildSoundHintTrigger

buildSoundHintTrigger(bridge, soundHint, position, currentTime)

Summary: Build audio trigger from a raw soundHint string.


audio/bridge/SubstanceAudioResolver

Summary: SubstanceAudioResolver.js - Resolve substance key → audio config Maps substance IDs to audio event configurations, supporting both sample-based fallback and procedural patch references.


resolveSubstanceAudio

Summary: Resolve a substance key to its audio configuration.


getSubstanceImpactSound

Summary: Get the impact sound event ID for a substance.


getSubstanceAmbientLoop

Summary: Get the ambient loop event ID for a substance.


getSubstanceCollisionSound

Summary: Get the collision sound event ID for a substance.


getSubstanceProceduralPatch

Summary: Get procedural patch ID for a substance (if defined).


getSubstanceParamMap

Summary: Get parameter mapping for procedural synthesis. Maps emitter params → synth params.


getSubstanceAudioDefaults

Summary: Get normalized default procedural params for a substance.


applySubstanceParamMap

applySubstanceParamMap(substanceId, params = {}, mapOverrides = null)

Summary: Apply a substance paramMap to source params. Supports runtime map override keys: _map_<src>_scale / _map_<src>_offset.


buildSubstancePreviewParams

buildSubstancePreviewParams(substanceId, overrides = {}, sourceParams = {})

Summary: Build preview/runtime-friendly patch params with shared defaults + paramMap transform.


buildSubstanceTriggerConfig

buildSubstanceTriggerConfig(substanceId, eventType, params = {})

Summary: Build a complete audio trigger config from substance + event params.


audio/bridge/WeatherAudioBridge

Summary: WeatherAudioBridge.js - Weather/Time → Ambient Audio Crossfades ambient audio loops based on WorldTime day/night cycle, seasons, and weather conditions.


createWeatherAudioBridge

Summary: Create a weather audio bridge.


tickWeatherAudio

tickWeatherAudio(bridge, worldTime, weather, currentTime)

Summary: Tick weather audio — check time of day and weather, crossfade ambients.


audio/bridge/index

Summary: engine/audio/bridge/index.js — Barrel export for audio bridges.


audio/core/AudioAssetManager

Summary: AudioAssetManager.js - Audio Asset Loading & Caching Fetches, decodes, and caches AudioBuffers. Supports pooling and streaming for large files (music).


createAudioAssetManager

Summary: Create an audio asset manager.


loadAudioAsset

Summary: Load and decode an audio file. Returns cached buffer if already loaded.


preloadAudioAssets

Summary: Preload multiple audio assets.


registerAudioBuffer

Summary: Register a pre-decoded AudioBuffer directly.


getAudioBuffer

Summary: Get a cached buffer (synchronous).


hasAudioBuffer

Summary: Check if a buffer is cached.


unloadAudioAsset

Summary: Remove a cached buffer.


clearAudioCache

Summary: Clear all cached buffers.


setAudioBaseUrl

Summary: Set base URL for relative asset paths.


destroyAudioAssetManager

Summary: Destroy the asset manager and clear all caches.


audio/core/AudioBus

Summary: AudioBus.js - Audio Category Bus A bus is a GainNode chain representing an audio category (sfx, music, ambient, voice, ui). Supports volume, mute, solo, and ducking (automatic volume reduction when another bus plays).


createAudioBus

createAudioBus(context, destination, config = {})

Summary: Create an audio bus.


setBusVolume

Summary: Set bus volume.


setBusMute

Summary: Mute/unmute the bus.


setBusDuck

Summary: Set duck amount (used for automatic ducking, e.g. voice ducks music).


getBusOutput

Summary: Get the bus's output node (for sources to connect to).


destroyAudioBus

Summary: Destroy a bus and disconnect it.


audio/core/AudioConcurrency

Summary: AudioConcurrency.js - Voice Management & Stealing Manages the maximum number of concurrent voices. When the limit is reached, uses priority-based voice stealing (quietest, oldest, or farthest).


createAudioConcurrency

Summary: Create a concurrency manager.


tickConcurrency

Summary: Tick concurrency — steal voices if over limit.


canAddVoice

Summary: Check if a new voice can be added, or which voice to steal.


destroyAudioConcurrency

Summary: Destroy concurrency manager.


audio/core/AudioDoppler

Summary: AudioDoppler.js - Doppler Effect Manually computes pitch shift from radial velocity between source and listener. Web Audio's built-in Doppler was removed from the spec, so we implement it ourselves. Doppler ratio = (speedOfSound + listenerRadialVelocity) / (speedOfSound + sourceRadialVelocity)


createAudioDoppler

Summary: Create a Doppler processor.


tickDoppler

tickDoppler(doppler, activeSources, listenerPos, listenerVel)

Summary: Tick Doppler — adjust pitch of all spatial sources based on relative velocity.


destroyAudioDoppler

Summary: Destroy Doppler processor.


audio/core/AudioEngine

Summary: AudioEngine.js - Core Audio Engine Central audio system for the engine. Manages AudioContext lifecycle, master gain, tick loop, and coordinates all audio subsystems. Usage: const engine = createAudioEngine({ maxVoices: 64 }); await initAudioEngine(engine); // In main loop: tickAudioEngine(engine, dt); // Cleanup: destroyAudioEngine(engine);


createAudioEngine

Summary: Create an audio engine instance.


initAudioEngine

Summary: Initialize the audio engine. Creates AudioContext and all subsystems. Must be called after a user gesture (click/tap) to comply with autoplay policy.


resumeAudioEngine

Summary: Ensure the AudioContext is resumed (call after user gesture).


tickAudioEngine

Summary: Tick the audio engine. Updates active sources, cleans up finished ones.


triggerAudioEvent

triggerAudioEvent(engine, eventId, options = {})

Summary: Trigger an audio event by ID.


playSound

playSound(engine, eventId, options = {})

Summary: Backward-compatible alias used by older bridge code.


playBuffer

playBuffer(engine, buffer, options = {})

Summary: Play a buffer directly (bypasses event resolution).


stopSource

stopSource(engine, sourceId, fadeOut = 0.05)

Summary: Stop a specific source by ID.


stopAllSources

Summary: Stop all active sources.


registerAudioEvent

Summary: Register an audio event descriptor.


unregisterAudioEvent

Summary: Unregister an audio event.


setMasterVolume

Summary: Set master volume.


setBusVolumeByName

Summary: Set volume for a specific bus.


destroyAudioEngine

Summary: Destroy the audio engine and release all resources.


audio/core/AudioEvent

Summary: AudioEvent.js - Audio Event Descriptors Defines how audio events are resolved: one-shots, random containers, sequences, and loops. An event maps an ID to concrete playback params. Event descriptor format: { type: 'oneshot' | 'random' | 'sequence' | 'loop', sounds: ['path1.ogg', 'path2.ogg'],  // Pool of sounds volume: [0.8, 1.0],       // Random range or fixed value pitch: [0.9, 1.1],        // Random range or fixed value bus: 'sfx',               // Target bus priority: 0,              // Voice stealing priority cooldown: 0,              // Min seconds between triggers maxInstances: 0,          // Max simultaneous (0 = unlimited) }


resolveAudioEvent

resolveAudioEvent(event, eventId, options, time)

Summary: Resolve an audio event descriptor to concrete play parameters.


createAudioEventDescriptor

Summary: Create an audio event descriptor.


audio/core/AudioListener

Summary: AudioListener.js - 3D Audio Listener Wraps the Web Audio API AudioListener, syncing camera position, orientation, and velocity for accurate 3D spatialization.


createAudioListener

Summary: Create an audio listener wrapper.


updateAudioListener

updateAudioListener(listener, position, forward, up, dt)

Summary: Update listener position, orientation, and velocity. Call once per frame with camera data.


getListenerPosition

Summary: Get the listener's current position.


getListenerVelocity

Summary: Get the listener's current velocity.


destroyAudioListener

Summary: Destroy the listener wrapper.