@awaken/core
The engine foundation: math, the entity–component World, transforms, scene serialization, and a pure skeletal-animation core. It has no dependencies - no WebGPU, no DOM, no Rapier - so every symbol here is unit-testable in isolation, and every other package builds on it.
Everything is re-exported from the package barrel:
ts
import { World, Transform, mat4FromTRS, serializeWorld } from "@awaken/core";The package exports a VERSION = "0.0.0" string constant. The modules below map one-to-one onto files in packages/core/src.
math
Column-major 4×4 matrices (WebGPU/WGSL convention) stored as Float32Array; vectors and quaternions are plain objects. All matrix functions write into an out argument and return it, so you can reuse buffers in hot loops.
| Symbol | Signature | Description |
|---|---|---|
Vec3 | { x: number; y: number; z: number } | 3-component vector (plain object). |
Quat | { x, y, z, w: number } | Quaternion (plain object), w = 1 identity. |
Mat4 | Float32Array (length 16) | Column-major 4×4 matrix. |
vec3 | (x?, y?, z?) => Vec3 | Construct a vector (defaults 0). |
quat | (x?, y?, z?, w?) => Quat | Construct a quaternion (defaults identity). |
snap | (v, step) => number | Round v to the nearest multiple of step (step ≤ 0 → unchanged). |
mat4Identity | (out?) => Mat4 | Write the identity matrix. |
mat4Multiply | (out, a, b) => Mat4 | out = a · b (column-major). |
mat4FromTRS | (out, t, r, s) => Mat4 | Compose translation/rotation/scale into a matrix. |
mat4Invert | (out, m) => Mat4 | Invert m (identity fallback for a singular matrix). |
mat4OrthoZO | (out, l, r, b, t, near, far) => Mat4 | Orthographic projection, WebGPU clip space (z ∈ [0,1]). |
quatFromEuler | (out, x, y, z) => Quat | Euler (XYZ, radians) → quaternion. |
quatToEuler | (q) => Vec3 | Quaternion → Euler angles (radians), inverse of quatFromEuler. |
quatMultiply | (out, a, b) => Quat | Quaternion product. |
quatFromAxisAngle | (out, ax, ay, az, angle) => Quat | Rotation of angle radians about an axis. |
quatNormalize | (out) => Quat | Normalise in place (guards against drift). |
quatRotateVec3 | (out, q, v) => Vec3 | Rotate a vector by a quaternion. |
Projection helpers perspectiveZO, lookAt, cameraPositionFromView, and transformPoint live in @awaken/render (the camera module), not here.
entity
An Entity is a plain number that packs a 24-bit id with a generation counter (id + gen · 2²⁴), so it stays below 2⁴⁸ and remains an exact JS integer. The generation lets a freed id be reused without stale handles silently resolving to the new occupant.
| Symbol | Signature | Description |
|---|---|---|
Entity | number | Packed id + generation handle. |
NULL_ENTITY | Entity (0) | The "no entity" sentinel (also used for "no parent"). |
entityId | (e) => number | Extract the low 24-bit id. |
entityGen | (e) => number | Extract the generation. |
EntityAllocator | class | Allocates/frees entities; allocate(), free(e), isAlive(e). Owned by World. |
component
Components are data-only (no methods) and described by a reflection spec, so they serialize and appear in the Inspector for free. defineComponent returns a ComponentType<T> that the World stores and the serializer walks.
| Symbol | Signature | Description |
|---|---|---|
FieldType | union | One of "number" | "bool" | "string" | "vec3" | "quat" | "color" | "enum" | "assetRef" | "entityRef" | "colliderShapes" | "scriptList" | "scriptParams" | "entityRefMap". |
FieldSpec | { name; type: FieldType; default; options?; showIf? } | One field's reflection metadata. options lists enum values; showIf(values) hides a field in the Inspector conditionally. |
ComponentType<T> | { name: string; fields: FieldSpec[]; create: () => T } | A registered component type. |
ComponentBase | { enabled?: boolean } | Base every component interface extends - the per-component enable toggle. |
ENABLED_FIELD | FieldSpec | The enabled field (type:"bool", default true) auto-appended to every component so it serializes; hidden from the Inspector body. |
defineComponent<T> | (name, fields, create) => ComponentType<T> | Define a component; appends ENABLED_FIELD automatically. |
The enabled flag follows Unity's Behaviour.enabled: it is stored only when explicitly false, and systems skip a disabled component. See Component Reference for how each registered type uses this.
world
World is the ECS container: a sparse-set store per component type, plus a parent/child hierarchy, per-entity name/active/static flags, and change-tracking for the renderer. Component lookups are by ComponentType, not string.
Entity lifecycle
| Method | Signature | Description |
|---|---|---|
createEntity | () => Entity | Allocate a new entity. |
destroyEntity | (e) => void | Free an entity, detach it from the hierarchy, and remove all its components. |
isAlive | (e) => boolean | Whether the handle still refers to a live entity. |
clear | () => void | Remove every entity and component (resets the allocator). |
Components
| Method | Signature | Description |
|---|---|---|
add<T> | (e, type, init?) => T | Add a component (created from type.create(), overlaid with init). |
get<T> | (e, type) => T | undefined | Read a component. |
has | (e, type) => boolean | Whether the entity has the component. |
remove | (e, type) => void | Remove a component. |
entitiesWith | (type) => Entity[] | Every entity carrying type. |
query | (...types) => Entity[] | Entities carrying all given types (iterates the smallest store). |
registry | Map<string, ComponentType> | Every component type the world has seen, keyed by name - the serializer's type list. |
Hierarchy, names & flags
| Method | Signature | Description |
|---|---|---|
setParent | (child, parent) => void | Reparent (NULL_ENTITY = detach). Rejects cycles. |
getParent | (e) => Entity | Parent, or NULL_ENTITY. |
getChildren | (e) => Entity[] | Direct children (a copy). |
getName / setName | (e) => string / (e, name) | Per-entity display name. |
isActive / setActive | (e) => boolean / (e, active) | This entity's own enable flag. |
isActiveInHierarchy | (e) => boolean | True only if the entity and every ancestor are active. |
isStatic / setStatic | (e) => boolean / (e, value) | The entity-level Static flag (non-moving scenery; drives batching + physics). See Static Batching. |
Change tracking
Two mechanisms let the renderer skip work on unchanged scenes:
| Member | Type | Description |
|---|---|---|
structureRev | number | Bumped on any structural change - entity create/destroy, component add/remove, active/static/parent change. A renderer compares it frame-to-frame; unchanged ⇒ the draw-cache topology is stable. Direct field writes (t.position) do not bump it. |
markMoved | (e) => void | Flag an entity whose Transform a system/script changed this frame. |
movedEntities | ReadonlySet<Entity> | The set of flagged entities (read by the render loop). |
clearMoved | () => void | Clear the moved set (called once per frame by the sim owner). |
This is what makes a scene of 15k static props plus one rotator cost O(1) per frame instead of O(15k) - see the render-frame performance notes.
transform
The Transform component (local position/rotation/scale) plus world-matrix computation.
| Symbol | Signature | Description |
|---|---|---|
Transform | ComponentType + interface | { position: Vec3; rotation: Quat; scale: Vec3 }. Defaults: pos 0, rot identity, scale 1. |
worldTRS | (world, e) => { position, rotation, scale } | The entity's world TRS, composing every ancestor. Physics uses this so a nested collider lands on its mesh, not near the origin. |
computeWorldMatrices | (world) => Map<Entity, Mat4> | World matrix for every entity with a Transform (full recompute; cycle-guarded). |
updateWorldMatrices | (world, out, moved) => Entity[] | Incremental: recompute only the moved entities + descendants into out, reusing every other cached matrix. Returns the entities touched. |
A Transform-less ancestor breaks the chain (the entity roots there rather than composing past the gap) - worldTRS and computeWorldMatrices agree on this so physics matches the render.
gameobject
A thin OO convenience wrapper over World + Entity, mirroring Unity's GameObject. Everything it does is a World call underneath, so you can mix the two freely.
| Symbol | Signature | Description |
|---|---|---|
GameObject | class | Wraps (world, entity). name, transform, parent, children accessors + addComponent/getComponent/hasComponent/removeComponent. |
createGameObject | (world, name?) => GameObject | Create an entity, name it, and ensure it has a Transform. |
serialize
Scene ↔ JSON. SCENE_VERSION = 4 is the current scene-graph format; older scenes migrate forward on load. See Data Formats for the full shape and migration chain.
| Symbol | Signature | Description |
|---|---|---|
SCENE_VERSION | 4 | Current scene-graph format version. |
SceneJSON | { version: number; entities: EntityJSON[] } | A serialized scene. |
EntityJSON | { id; name; parent; active?; static?; components } | One serialized entity; active/static omitted at their defaults. |
serializeWorld | (world) => SceneJSON | Serialize every entity in the world. |
serializeEntities | (world, entities, types?) => SceneJSON | Serialize a specific set (parent links outside the set become null). |
instantiateScene | (world, json, types) => Entity[] | Add a scene's entities to a world (no clear); returns the new root entities. Migrates + clones object fields so instances never alias. |
applySceneToWorld | (world, json, types) => void | world.clear() then instantiateScene. |
deserializeWorld | (json, types) => World | Build a fresh World from a scene. |
migrateScene | (json) => SceneJSON | Forward-migrate a loaded scene to SCENE_VERSION (the single chokepoint every load passes through). |
applyMigrations | (json, migrations, current) => SceneJSON | Pure migration runner (injectable/testable). |
SCENE_MIGRATIONS | Record<number, SceneMigration> | The registered v1→v2→v3→v4 steps (collider + multi-script format changes). |
instantiateScene runs in three passes - create entities, apply components (remapping entityRef/scriptList handles), then wire the hierarchy - so references resolve regardless of entity order.
animation
A pure animation core with zero GPU dependency: skinning (clip + time → local pose → world bone matrices → skinning matrices → CPU-skinned positions), pose blending for crossfades, per-frame root-motion extraction, a state-graph evaluator, and foot IK. The runtime's animationSystem wires it to the renderer.
| Symbol | Signature / Shape | Description |
|---|---|---|
Skeleton | { bones: Bone[] } | A bone hierarchy; each Bone has name, parent (−1 = root), bind TRS, and inverseBind matrix. |
AnimationClip | { name; duration; channels: AnimationChannel[] } | A named clip; each channel drives one bone's translation/rotation/scale over keyframes. |
SkinData | { meshId; joints; weights; basePositions } | Per-vertex skin binding + read-only rest geometry for one skinned mesh. |
LocalPose | { t: Vec3[]; r: Quat[]; s: Vec3[] } | Per-bone local TRS for one sampled instant. |
Interp | "linear" | "step" | Keyframe interpolation (cubicspline sampled as linear). |
samplePose | (skel, clip, time) => LocalPose | Sample a clip → per-bone local pose (undriven bones keep their bind pose). |
poseMatrices | (skel, pose) => Mat4[] | Forward kinematics → per-bone world matrices. |
skinningMatrices | (skel, world) => Mat4[] | world · inverseBind per bone (the matrices LBS multiplies by). |
skinPositions | (positions, joints, weights, skin, influences?) => Float32Array | Linear-blend-skin vertex positions on the CPU. |
blendPoses | (a, b, w) => LocalPose | Per-bone slerp/lerp blend of two local poses - the primitive behind clip crossfades. |
sampleRootMotion | (clip, time) => { x, z } | Sample a clip's baked horizontal root-motion track (the per-frame delta the controller applies). |
AnimGraph / nextTransition / graphState / conditionPasses | data + evaluator | Pure animation state-graph: states, declarative-condition transitions, "*" any-state, one-shot triggers. |
applyFootIK / FootIKConfig | (skel, pose, cfg, groundY) => Mat4[] | Two-bone leg IK + pelvis adjust + plant weight that grounds planted feet. |
vec3Lerp, quatSlerp, sampleVec3, sampleQuat | helpers | Interpolation + channel sampling primitives. |
particles
The particle system's data + simulation core - also GPU-free. simulateEmitter is a pure, deterministic integrator: it advances a live-particle pool and writes a flat instance buffer that the renderer uploads. This is the seam a future GPU-compute backend would replace; everything above it (the effect asset, the component, the 16-float record) stays put.
| Symbol | Signature / Shape | Description |
|---|---|---|
ParticleEffect | config object | One effect's full definition: emission (rate, bursts, maxParticles, duration, loop, oneShot), per-particle init (lifetime+variance, shape, speed+variance, spreadDeg), forces (gravity, drag), look over life (color gradient, sizeStart, sizeCurve, rotationSpeed), and render (blend, texture, worldSpace, softness, stretch). |
EmissionShape | { kind: "point" } | { kind: "sphere"; radius } | { kind: "box"; half } | { kind: "cone"; angleDeg; radius } | Where particles spawn, in emitter-local space. |
ColorGradient / Curve | { stops: { t; color }[] } / { keys: { t; v }[] } | Colour and scalar over normalized lifetime (t 0→1, sorted). Sampled on the CPU, so the GPU never needs a LUT. |
ParticleEmitter | ComponentType<{ effect: string; playing: boolean }> | Component placing an effect on an entity; effect is a ParticleEffect asset id. See Components. |
EmitterState | SoA pool | One emitter's live particles (position/velocity/life/rotation/size arrays) + a seeded PRNG. Side-state - never serialized. |
makeEmitterState | (cfg, seed?) => EmitterState | Allocate a pool sized to cfg.maxParticles with a deterministic PRNG (never Math.random). |
simulateEmitter | (state, cfg, dt, worldMatrix, out) => count | Advance by dt and write count × PARTICLE_FLOATS floats into out; returns the live count. out must hold state.capacity × PARTICLE_FLOATS. |
sampleGradient / sampleCurve | (g, t) => [r,g,b,a] / (c, t) => number | Linear sampling, t clamped to 0..1. |
PARTICLE_FLOATS | 12 | Per-particle GPU instance record: pos.xyz, size, rgba, rotation, vel.xyz. |
See also
- GameObjects & Components - the entity/component model in the editor.
- Transforms & Hierarchy - local vs world space and parenting.
- Component Reference - every registered component built with
defineComponent. - Data Formats & Versions - the
SceneJSONshape and migrations. - @awaken/render - the renderer that consumes
World+ world matrices.