← Natura·Docs/Start here
Contents · Schema

Natura Schema -- the formal data shapes

The precise types the framework passes around, so every system agrees on the wire format (operator: "schema?"). Notation is TypeScript-ish for clarity; Natura itself is plain JS (README conventions). This is the contract: implement a System to this shape and it plugs into any World; expose Fields to this shape and any downstream system can read them. Pairs with README (conventions) + ALGORITHMS (per- system field lists).

Core types

// A SYSTEM DEFINITION -- the unit of simulation. Plain object; all methods pure + deterministic.
interface System {
  name:   string;                       // unique key; how others read your fields (ctx.fields[name])
  domain: 'physical' | 'life' | 'mind' | 'society';        // ONTOLOGY layer
  scale:  'cosmic'|'planetary'|'regional'|'weather'|'ecological'|'local'|'micro';   // scale-ladder rung
  params?: object;                      // preset defaults (like a Terrain Engine preset)
  init(rng: Rng, params: object, clock: Clock): State;     // build initial state (seeded, deterministic)
  step(state: State, ctx: Ctx, dt: Seconds): State;        // advance one tick; return (or mutate+return) state
  fields?(state: State): Fields;        // OPTIONAL: what you expose for coupling (omit if you produce nothing)
  events?(state: State): Event[];       // OPTIONAL: discrete happenings this tick
}

type State   = any;                     // opaque to the framework; a system owns its own shape
type Rng     = () => number;            // uniform [0,1); mulberry32, seeded (never Math.random)
type Seconds = number;

// The CONTEXT handed to step() -- the only way a system sees the world outside itself.
interface Ctx {
  clock:  Clock;
  rng:    Rng;                          // the world's shared stream (per-system streams come from init)
  fields: { [systemName: string]: Fields };   // PRE-STEP snapshot of every system's fields (1-frame delayed)
  world:  { [name: string]: { def: System, state: State, params: object } };  // escape hatch; prefer fields
  dt:     Seconds;
}

// The CLOCK -- one time, every cadence derived by phase.
interface Clock {
  t: Seconds;                           // absolute time
  advance(dt: Seconds): Seconds;
  phase(period: Seconds): number;       // fraction [0,1) through a cycle of that length
  dayPhase(): number;   yearPhase(): number;   seasonPhase(): number;   // 0..1
  daylight(): number;   // 0 night .. 1 noon
  season(): 'spring'|'summer'|'autumn'|'winter';
  years(): number;      eons(): number; // absolute, for deep-time systems
  units: { SECOND:number, MINUTE:number, HOUR:number, DAY:number, WEEK:number,
           MONTH:number, YEAR:number, DECADE:number, CENTURY:number, MILLENNIUM:number, EON:number };
}

Fields (the coupling wire format)

fields() returns a plain object of named quantities. Rules:

interface Fields { [quantity: string]: number | Float32Array | Entity[] | any; N?: number; }

// Concrete examples (from ALGORITHMS.md):
// weather.fields()    -> { N, rain, moisture, temp, cloud, pressure, windU, windV, land: Uint8Array }
// terrain.fields()    -> { N, elevation, slope, flow, biome: Uint8Array, seaLevel }
// vegetation.fields() -> { N, biomass, canopy, food, fuel, litter, greenness }
// ecosystem.fields()  -> { populations: {[species]: number}, herds: Entity[] }
// stellar.fields()    -> { luminosity, radius, temperature, insolation }   // all scalars

Entity (for agent-based systems -- life-cycle, ecosystem)

The ONTOLOGY's "bag of STATE + the processes it's subject to." Not a class hierarchy -- LAYERS.

interface Entity {
  id:      number;
  kind:    'organism' | 'matter' | 'aggregate';   // ONTOLOGY entity kind
  species?: string;                                // for organisms
  pos:     [number, number] | [number, number, number];   // world coords [-1,1]
  vel?:    [number, number, number];               // for movers (boids)
  state: {                                         // the bag
    energy?: number; water?: number; mass?: number; age?: Seconds; health?: number;
    stage?:  'egg'|'seed'|'juvenile'|'adult'|'senescent'|'dead';   // LIFE-CYCLE state machine
    sex?:    'm' | 'f';
    [k: string]: any;                              // system-specific extras
  };
  layers?: ('physical'|'life'|'mind'|'society')[]; // which domains this entity participates in
}

Event

interface Event { system: string; t: Seconds; kind: string; [payload: string]: any; }
// e.g. { system:'weather', t, kind:'storm', x, y, str }
//      { system:'life',    t, kind:'birth', species, pos }
//      { system:'fire',    t, kind:'ignition', x, y }

World (the runner)

// SPEC -- what you pass to build a world
interface WorldSpec {
  seed:   number;
  t0?:    Seconds;                                 // start time (default 0)
  systems: (string | System)[];                    // a registered name, or a System object inline
  // couplings are IMPLICIT via ctx.fields for now; an explicit wiring map is a future refinement:
  // couplings?: { from: string, field: string, to: string, as: string }[]
}

// INSTANCE -- what World(spec) returns
interface World {
  clock:  Clock;
  seed:   number;
  step(dt: Seconds): { t: Seconds, events: Event[] };
  run(steps: number, dt: Seconds): Event[];
  fields(): { [systemName: string]: Fields };
  get(name: string): State | undefined;
  has(name: string): boolean;
  systems: string[];
  events(): Event[];                               // last tick's events
}

Invariants (what the framework guarantees / requires)