Phaser AE

Procedural models

Free packv3.0.0-53DLogic

A 3D model as data: a tree of primitives and smooth forms, each sized, posed and painted, with named clips that move them. Nothing is loaded from a file, so a model is something you can write by hand, generate, edit in a tool and read in a diff.

This is a Free pack. Every phaser.io account can use it in their games.

Released October 14, 2026 · updated October 19, 2026

Procedural models: a screenshot of its demo
A note about these graphics

Expansion packs don't come with graphics. This screenshot is one way of drawing what the pack does, made for its demo: none of the sprites, tiles or backgrounds in it are in the pack.

That's by design. A pack is a game's logic, mechanics and effects, and it works with whatever art you give it. Some packs draw effects, backdrops or 3D scenes of their own, but none carries a game's sprites.

So the look is yours. Make the art on phaser.io, bring your own, or let your agent draw it in code: the same pack can look like anything.

The pack

model3d

function model3d(): Pack<{ models: Model3dApi }, {}, {}, ModelSpawn>

The model-3d pack: builds procedural models from specs and plays their clips. Requires a 3D world. Every custom mesh (a lathe, an extrude, a tube) is registered ONCE per distinct profile, so twenty of one model share its meshes and its draws.

Helpers

cleanModelSpec

function cleanModelSpec(raw: unknown): ModelSpec

Tidy anything spec-shaped into a valid ModelSpec. TOTAL: never throws, never returns an invalid model. Strings are coerced, numbers clamped, unknown shapes become boxes, unknown materials take the first one, unknown parents become roots, duplicate ids are suffixed, cycles are broken, and every list is capped. An agent's or a file's output goes through here before the engine ever sees it.

emptyModel

const emptyModel = (name = 'model'): ModelSpec=> …

An empty model.

MODEL_CHANNELS

const MODEL_CHANNELS: readonly ["x", "y", "z", "yaw", "pitch", "roll", "sx", "sy", "sz"]

The per-part transform channels a clip may drive.

MODEL_DEFAULT_MATERIAL

const MODEL_DEFAULT_MATERIAL: ModelMaterial

The default surface, used when a model names no material at all.

MODEL_DETAILS

const MODEL_DETAILS: ReadonlyArray<ModelDetail>

Every ModelDetail value, coarsest first.

MODEL_DIE_SIDES

const MODEL_DIE_SIDES: readonly [4, 6, 8, 10, 12, 20]

The sides a die can have.

MODEL_LIMITS

const MODEL_LIMITS: { readonly parts: 160; readonly materials: 32; readonly clips: 24; readonly tracks: 96; readonly keys: 64; readonly points: 64; readonly patternColors: 3; readonly name: 60; readonly id: 40; readonly size: 200; }

Hard caps a spec is clamped to; a model past these is a scene, not a model.

MODEL_PATTERN_DEFAULTS

const MODEL_PATTERN_DEFAULTS: Readonly<Record<ModelPatternKind, { scale: number; amount: number; count: number }>>

The fields a pattern leaves out, per kind.

MODEL_PATTERNS

const MODEL_PATTERNS: readonly ["stripes", "bands", "swirl", "rings", "segments", "checker", "grid", "dots", "speckle", "sprinkles", "spots", "grain", "pips", "numbers"]

Every surface pattern a material may wear. The material's own color is the GROUND and the pattern paints marks over it; render-3d draws them (its Pattern3dKind is this same list).

MODEL_SCATTER_MODES

const MODEL_SCATTER_MODES: readonly ["surface", "grid", "spiral", "ring", "line", "volume"]

Where a scatter's copies go. surface, grid and spiral cover the part's PARENT; ring, line and volume fill its own box.

MODEL_SHAPES

const MODEL_SHAPES: readonly ["box", "roundedBox", "sphere", "capsule", "cylinder", "cone", "torus", "wedge", "disc", "plane", "ring", "tetrahedron", "octahedron", "icosahedron", "dodecahedron", "torusKnot", "lathe", "extrude", "tube", "sweep", "vessel", "leaf", "scatter", "coat", "die", "none"]

Every primitive a part may be. none is a pure pivot — a joint or a group with nothing of its own to draw.

modelBounds

function modelBounds(spec: ModelSpec): { min: [number, number, number]; max: [number, number, number]; parts: number }

The world-space axis-aligned box a spec fills at rest, ignoring rotation, plus its part count — for framing a camera and for a readout.

modelContacts

function modelContacts(raw: ModelSpec | unknown): ModelContacts

Check that a model holds together on its real meshes, at rest: the parts that float (reach the floor through nothing that touches), and the parts that poke through the walls of a container they sit in (a vessel, or any part with a see-through material). Meters. Slow for a big model (a second or two): a check, not a per-frame thing.

sampleClip

function sampleClip(clip: ModelClip, t: number): Map<string, ModelPose>

Every part's delta pose at time t of a clip, keyed by part id. Parts the clip does not touch are absent.

sampleTrack

function sampleTrack(track: ModelTrack, t: number): number

The value of one track at time t, honouring its ease; before the first key it is the first key's value, after the last the last's.

Functions

buildForm

function buildForm(p: ModelPart, ctx: FormContext = {}): FormPiece[]

Build a form: its pieces, in the unit box.

cleanPattern

function cleanPattern(raw: unknown): ModelPattern | undefined

Tidy anything pattern-shaped, or drop it: an unknown kind or no usable color is no pattern at all. Only the fields given are kept, so a cleaned spec stays as short as it was written. TOTAL: never throws.

dieBox

function dieBox(sides: typeof MODEL_DIE_SIDES[number], chamfer = 0.12): [number, number, number]

A die's natural box at its chamfer, its longest side 1.

drawnMeters

function drawnMeters(p: ModelPart, ctx: FormContext = {}): Soup

A part's surface in meters, centred, exactly as it is drawn (a scatter or a coat is built on it).

expandParts

function expandParts(spec: ModelSpec): ModelPart[]

The parts the engine actually builds: every mirror part expanded into itself plus its twin across x (id <id>__m, name suffixed, children duplicated beneath the twin). The order is parents before children, so a builder can walk it once.

fillUnit

function fillUnit(v: Float32Array): Float32Array

A primitive filling its unit box. Most do already; a torus is as tall as its tube (a third of the box), a capsule as wide as its caps, a cylinder tapered past 1 wider than it, the polyhedra smaller. Stretched to fill it, a part's size is the box it really fills, so what is placed by the box (a seed on a bagel) sits on the surface. The height is stretched on its own and the width and depth together, so a six-sided cylinder stays a regular hexagon (its corners reach the box, its flats do not). Flat ones (a plane, a circle) stay as they are.

formKey

function formKey(p: ModelPart, ctx: FormContext = {}): string

Everything that shapes a form's meshes — its parent's and tools' shapes too — as a registry key.

isForm

function isForm(p: ModelPart): boolean

Is this part a form (built at real size), rather than a plain unit primitive?

patternSurface

function patternSurface(part: ModelPart): { around: number; along: number; uAlong?: boolean } | null

A pattern measured on the SURFACE (space: 'surface') needs the mesh's surface size: how far its uv's around coordinate goes round and its along coordinate goes along, in world units at the part's spec size, and whether the mesh's u is the along one (a tube's is). Null when the shape's uv says nothing of the kind (a box): the pattern is then measured in the part's frame. A new shape with a meaningful uv adds its case here.

plainVerts

function plainVerts(p: ModelPart): Float32Array

A plain (not a form) part's unit vertices: a unit-fitted lathe, extrusion or tube, or a primitive filling its box.

Constants

deg

const deg = (d: number): number=> …

Degrees → radians.

DETAIL

const DETAIL: Record<ModelDetail, { around: number; maxTurn: number }>

Segments round, and the most degrees a curve turns between samples, at each detail.

fullPattern

const fullPattern = (p: ModelPattern): ModelPatternFull=> …

Fill in a pattern's defaults: what the engine draws.

MODEL_DIE_BOX

const MODEL_DIE_BOX: Readonly<Record<typeof MODEL_DIE_SIDES[number], ReadonlyArray<readonly [number, number, number]>>>

A die's natural box, its longest side 1, at chamfer 0, 0.1, 0.2, 0.3 and 0.4 (a deeper chamfer rounds it toward a ball): the cleaner sizes a die to it, so it is never stretched. geometry-3d's dieSoup makes them; a test holds the two equal.

restDelta

const restDelta = (): ModelPose=> …

A pose with nothing moved: the identity delta.

Types

FormContext

interface FormContext

What a part needs from the rest of its model to be built: its parent (a scatter's or coat's surface) and its tools.

  • parent?: ModelPart | undefined — The part this one hangs from, whose surface a scatter or coat covers; absent for a root or for a part that needs none.
  • tools?: ReadonlyArray<ModelPart> | undefined — The tool children whose shapes are cut out of this part; absent when it has no cuts.
  • of?: ((p: ModelPart) => FormContext) | undefined — The context of any other part of the model (a parent that is itself a coat needs its own parent).

FormPiece

interface FormPiece

One mesh of a part: its main surface, or a second surface with its own material.

  • role: 'main' | 'inside' | 'fill' | 'caps' | 'cap' | 'cut' — What it is: the part's surface, a vessel's inside, its contents, flat caps, a slice's face, a cut's faces.
  • material: string — The material id it is painted with.
  • verts: Float32Array — Interleaved vertices in the unit box.

Model3dApi

interface Model3dApi

game.models.

  • define(name: string, spec: ModelSpec | unknown): void — Name a spec, so game.spawn({ model: name }) builds it. Cleaned now, so a bad spec fails here rather than at the first spawn.
  • has(name: string): boolean — Is there a model by this name?
  • build(spec: ModelSpec | unknown, at?: ModelPlacement): ModelInstance — Build a spec into live objects. The spec is cleaned first, so anything spec-shaped is safe to pass.
  • of(root: Handle): ModelInstance | null — The model built on a spawned object (its root), or null: its parts, meshes and clip player.
  • clean(raw: unknown): ModelSpec — Tidy anything spec-shaped into a valid spec without building it.
  • bounds(spec: ModelSpec): ReturnType<typeof modelBounds> — The rest-pose bounds of a spec, for framing a camera.
  • readonly count: number — Live instances.

ModelAxis

type ModelAxis = 'x' | 'y' | 'z'

One of a part's own axes.

ModelChannel

type ModelChannel = typeof MODEL_CHANNELS[number]

One of MODEL_CHANNELS.

ModelClip

interface ModelClip

A named animation: several tracks over one duration.

  • id: string — Unique within the model: idle, walk, open.
  • name: string — What a person calls it.
  • duration: number — Seconds.
  • loop?: boolean — Wrap around at the end. Default true.
  • tracks: ModelTrack[] — The channels it drives.

ModelCoat

interface ModelCoat

A coat: a layer over the part's PARENT's surface (icing, glaze, chocolate, frosting, melted cheese).

  • thickness: number — Meters.
  • from?: number — Where its edge sits, 0 (bottom) … 1 (top) of the parent's height. Default 0.5.
  • drips?: number — Drips running down from the edge.
  • dripDepth?: number — The longest drip, meters.
  • wave?: number — How much the edge waves, meters.
  • waves?: number — Waves round the edge. Default 7.
  • under?: boolean — Coat the part below the edge instead.
  • seed?: number — A different set of drips and waves: a whole number from 0 to 9999. Default 3.

ModelContacts

interface ModelContacts

What modelContacts found: the parts that float and the parts that poke through a container. Both are empty for a sound model.

  • floating: ModelFloating[] — Parts that do not reach the floor through touching parts.
  • poking: ModelPoking[] — Parts that poke through the wall of a container they sit in.

ModelDeform

interface ModelDeform

Reshaping a part along its own y axis, at its real size. Applied in this order: squash, lobes, taper, twist, noise, bend.

  • bend?: number — Degrees the y axis curves through as an arc (banana, cucumber, horn).
  • bendAxis?: 'x' | 'z' — It bends toward +x (z, default) or toward +z (x).
  • taper?: [number, number] — The section's scale at the top relative to the bottom, [x, z] (carrot, cone, trunk).
  • twist?: number — Degrees the section turns about y from bottom to top (licorice, rope).
  • lobes?: { count: number; depth: number; sharp?: number } — The section pushed in and out round y: count lobes, depth 0…0.95, sharp 0 (waves) … 1 (narrow creases) — pumpkin, pepper, garlic.
  • noise?: { amount: number; scale?: number; seed?: number } — Smooth lumps over the surface: amount in meters, scale the size of a lump in meters, seed (potato, rock).
  • squash?: [number, number] — The section's scale [x, z]: an oval.

ModelDetail

type ModelDetail = 'low' | 'medium' | 'high'

How finely a curved part is cut: low about 12 segments round, medium 24 (the default for a form), high 40.

ModelDie

interface ModelDie

A die: a gaming die resting on a face, its edges and corners chamfered, each face numbered (opposite faces add to sides + 1; the top shows the highest). A material with the pips or numbers pattern paints the faces.

  • sides: typeof MODEL_DIE_SIDES[number] — Number of faces: one of MODEL_DIE_SIDES; a spec with any other value is cleaned to 6.
  • chamfer?: number — 0 (sharp) … 0.4: how much of each edge is chamfered away. Default 0.12.

ModelEase

type ModelEase = 'smooth' | 'linear' | 'step'

How a track moves between keys.

ModelFloating

interface ModelFloating

A part that reaches nothing: how far it is from what should hold it (its nearest drawn ancestor, or the nearest part that is held), and the world move, meters, that would close the gap (toward: from its nearest point to that part's surface).

  • part: string — Id of the part that floats.
  • from: string | null — Id of the part that should hold it: its nearest drawn ancestor, or the nearest held part; null when nothing does and the gap is to the floor.
  • gap: number — Distance in meters from the part's nearest point to that part's surface.
  • toward: [number, number, number] — The world move in meters, [x, y, z], from the part's nearest point to that part's surface, that would close the gap.

ModelInstance

interface ModelInstance

A built model: the handles, and the clip player.

  • readonly spec: ModelSpec — The spec it was built from, cleaned.
  • readonly root: Handle — The model's origin: move, turn or scale this and the whole model follows. Drawn by nothing.
  • readonly parts: Readonly<Record<string, Handle>> — Every part's PIVOT handle by id (mirror twins under <id>__m). Attach a weapon to parts.hand; read a wheel's world pose here.
  • readonly meshes: Readonly<Record<string, Handle>> — Every part's DRAWN handle by id — the same object as the pivot unless the part has an offset. Recolour with .tint.
  • readonly handles: ReadonlyArray<Handle> — Every handle the model owns, root first.
  • readonly clip: string | null — The clip playing, or null.
  • readonly playing: boolean — Advancing (a clip is playing and speed is not 0).
  • time: number — Seconds into the clip. Write it to scrub; the pose updates next step.
  • play(clip: string, opts?: PlayOptions): void — Play a clip by id. An unknown id stops.
  • stop(): void — Stop and return every part to its rest pose.
  • pose(clip: string, time: number): void — Hold one moment of a clip, without playing: a scrubber, a pose picker.
  • kill(): void — Kill every handle. The instance is inert afterwards.

ModelKey

interface ModelKey

One key on a track: a time in seconds and a value.

  • t: number — Seconds from the start of the clip, 0 … duration.
  • v: number — The channel's value at that moment: a DELTA from rest for x/y/z (units) and yaw/pitch/roll (degrees), a MULTIPLIER for sx/sy/sz.

ModelLeaf

interface ModelLeaf

A leaf: a thin blade along +y, its face toward +z.

  • widths?: number[] — Half-widths along its length, base to tip, 0…1 (smooth between). Default a classic leaf.
  • fold?: number — Degrees of V folded along the midrib.
  • curl?: number — Degrees it curls along its length, toward its face.
  • twist?: number — Degrees it twists from base to tip.

ModelMaterial

interface ModelMaterial

A surface: one colour and a finish. Parts point at materials by id, so recolouring a hat is one edit.

  • id: string — Unique within the model. Short and legible: skin, brass, visor.
  • color: string — CSS hex color, #rrggbb: the whole surface, or the ground under a pattern.
  • rough?: number — Roughness 0 (mirror) … 1 (chalk). Default 0.7.
  • metallic?: number — Metalness 0 or 1; anything between reads as neither. Default 0.
  • emissive?: number — Self-illumination 0 … 1 — lamps, eyes, screens. Default 0.
  • unlit?: boolean — Take no lighting at all: flat, cartoon, sprite-like. Default false.
  • opacity?: number — 0.05 … 1: how solid it is. Below 1 what is behind shows through: glass 0.2 – 0.4, a drink 0.6 – 0.85, ice 0.7. A see-through part is drawn after the solid ones and casts no shadow. Default 1.
  • pattern?: ModelPattern — Marks painted over the color: stripes, speckle, sprinkles… See ModelPattern. Default none.

ModelPart

interface ModelPart

One primitive in the tree.

  • id: string — Unique within the model. Legible and stable: torso, arm-l, wheel-fr. Tracks address parts by it.
  • name: string — What a person calls it.
  • parent?: string | null — The part this one hangs from, or null/omitted for a root part. Unknown parents become roots.
  • shape: ModelShape — The primitive.
  • shapeOptions?: Record<string, number | boolean> — Options for the primitive, as geometry-3d takes them: segs, rings, rTop/rBottom (cylinder taper), open, arc, major/minor (torus: the tube's share of the radius), r (a rounded box's corners; a capsule's round ends, 0…0.5 of its height each), inner (ring), detail (polyhedra). Omitted values take the shape's defaults.
  • profile?: ModelPoint2[] — lathe, vessel: the profile to revolve about y, [radius, y] pairs bottom to top (a third value 1 marks a corner). Its shape counts, not its scale: the part's size sizes it.
  • outline?: ModelPoint2[] — extrude: the outline to extrude along y, [x, z] pairs within −0.5…0.5, wound either way (a third value 1 marks a corner).
  • bevel?: number — extrude: bevel radius 0…0.3 of the unit; default 0.
  • path?: ModelPoint3[] — tube: the polyline to sweep along; sweep: the control points of its smooth path. [x, y, z] within the unit box (a fourth value 1 marks a corner).
  • radius?: number — tube: tube radius as a fraction of the unit; default 0.16.
  • position: [number, number, number] — The pivot's position in the PARENT's frame (world units at the root).
  • rotation?: [number, number, number] — Yaw, pitch, roll in DEGREES about the pivot. Default [0, 0, 0].
  • size: [number, number, number] — Width, height, depth in world units: the box the part fills. Every primitive fills its unit box (a torus is as tall as its size says, a capsule as wide) and is scaled to this.
  • offset?: [number, number, number] — The mesh CENTRE relative to the pivot, in the part's own frame. A leg pivots at the hip: offset [0, -h/2, 0]. Default [0, 0, 0].
  • material: string — Material id. An unknown id takes the model's first material.
  • color?: string — A colour override for this part alone, CSS hex.
  • mirror?: boolean — Also build a mirror twin across x, id <id>__m, with its own mirrored children. Default false.
  • visible?: boolean — Drawn at all; a none part is never drawn regardless. Default true.
  • detail?: ModelDetail — How finely it is cut. Setting it (or any field below) builds the part as a smooth FORM at its real size.
  • facets?: boolean — Flat shading: one normal a face, the faceted look (gems, rocks, a deliberately low-poly style).
  • smooth?: boolean — False: a profile, outline or path is straight lines between its points rather than a smooth curve.
  • deform?: ModelDeform — Reshape it: bend, taper, twist, lobes, noise, squash.
  • caps?: string — A second material for its flat end caps (an extrude's top and bottom, a lathe's sealed ends).
  • sweep?: ModelSweep — sweep: how it is swept.
  • vessel?: ModelVessel — vessel: its wall, inside and contents.
  • leaf?: ModelLeaf — leaf: its shape.
  • die?: ModelDie — die: its sides and chamfer.
  • scatter?: ModelScatter — scatter: the copies and where they go.
  • coat?: ModelCoat — coat: the layer.
  • slice?: ModelSlice[] — Planes cutting it (up to 3): a half kiwi, a cake slice, a cut lemon.
  • cut?: string[] — Ids of child parts (marked tool) cut OUT of this one: holes in cheese, a bite from an apple.
  • cutMaterial?: string — Material of the faces a cut leaves (default the part's).
  • tool?: boolean — A shape used only to cut its parent: never drawn.

ModelPattern

interface ModelPattern

Marks painted over a material's color: a watermelon's stripes, a cookie's speckle, a donut's sprinkles. It is drawn in each part's own frame, at the part's size in the spec, so it sticks to the part when the part turns, moves, squashes in a clip or is scaled with the model, and a stripe is as wide on a big part as on a small one sharing the material. A part's color changes the ground, never the marks.

  • kind: ModelPatternKind — What it is. stripes run along the axis, count of them around it; bands run across it, stacked along it; swirl winds round it (a spiral on a flat face); rings circle it; segments are wedges round it with thin lines between; checker; grid is thin lines; dots are even polka dots; speckle tiny random flecks; sprinkles short random rods; spots big soft blotches; grain wavy lines along the axis (wood). pips and numbers paint a die's faces: each face's pips (up to nine, then its number) or its number, in the first color, amount how bold. On any other shape they draw nothing.
  • colors: string[] — The marks' colors, CSS hex: one to three. speckle, sprinkles, dots and spots pick among them at random; the rest take turns.
  • scale?: number — World units (meters): one repeat, or one mark's spacing for dots, speckle, sprinkles and spots. Default per kind.
  • count?: number — stripes, segments, swirl: how many go round the axis, whole so they meet. Default per kind.
  • amount?: number — 0…1, how much of the surface the marks take: a stripe's width, a dot's size, how dense the speckle is. Default per kind.
  • axis?: ModelAxis — The part's own axis the pattern runs along or around. Default y.
  • angle?: number — Degrees: turns the pattern about its axis. On bands it tilts them instead, for diagonal stripes. Default 0.
  • pixel?: number — 0 draws smooth edges; n draws the pattern in square pixels, n to a repeat — the pixel-texture look. Default 0.
  • seed?: number — A different scatter for the random kinds. Default 0.
  • space?: 'part' | 'surface' — Where it is measured. part (the default): in the part's own frame, about axis. surface: along the part's surface, so it follows a swept tube, a lathe's profile or a bent shape (a candy cane's swirl round its hook); the length is the axis, so axis is not used. A shape whose surface has no length and round (a box) uses part.

ModelPatternFull

type ModelPatternFull = Required<ModelPattern>

A pattern with every field filled in from MODEL_PATTERN_DEFAULTS.

ModelPatternKind

type ModelPatternKind = typeof MODEL_PATTERNS[number]

One of MODEL_PATTERNS.

ModelPlacement

interface ModelPlacement

Where a built model stands.

  • x?: number — World x. Default 0.
  • y?: number — World y — the model's origin, usually its feet. Default 0.
  • z?: number — World z. Default 0.
  • yaw?: number — Yaw, radians. Default 0.
  • scale?: number — Uniform scale of the whole model. Default 1.
  • layer?: string — Layer for every part. Default: the root's layer when root is given, else the world's first.
  • root?: Handle — An object to build the model on: it becomes the model's root, so its place, turn and scale are the model's and x, y, z, yaw and scale here are ignored. The model dies with it, and root.play('walk') plays a clip. This is how game.spawn({ model }) (core-3d) builds one. Default: a new root at x, y, z.

ModelPoint2

type ModelPoint2 = [number, number] | [number, number, number]

A 2D control point, [a, b], with a third value 1 to make it a sharp corner.

ModelPoint3

type ModelPoint3 = [number, number, number] | [number, number, number, number]

A 3D control point, [x, y, z], with a fourth value 1 to make it a sharp corner.

ModelPoking

interface ModelPoking

A part that pokes through the wall of the container it sits in, and by how much.

  • part: string — Id of the part that pokes out.
  • through: string — Id of the container it pokes through: a vessel, or any part with a see-through material.
  • by: number — How far it passes through the wall, in meters.

ModelPose

interface ModelPose

A part's rest pose in its parent's frame, in the engine's units (radians).

  • x: number — Position x.
  • y: number — Position y.
  • z: number — Position z.
  • yaw: number — Yaw, radians.
  • pitch: number — Pitch, radians.
  • roll: number — Roll, radians.
  • sx: number — Scale x multiplier.
  • sy: number — Scale y multiplier.
  • sz: number — Scale z multiplier.

ModelScatter

interface ModelScatter

A scatter: many copies of one small thing, merged into one mesh.

  • mode: ModelScatterMode — How the copies are placed; see MODEL_SCATTER_MODES.
  • item: ModelScatterItem — The copy to make many of.
  • count?: number — How many (surface, spiral, ring, line, volume).
  • rows?: number — grid: rows along the parent's height and copies round it.
  • columns?: number — grid: copies round the parent in each row. Default 14.
  • region?: 'all' | 'top' | 'side' | 'bottom' | { from: number; to: number } — Which part of the parent's surface: all, top, side, bottom, or { from, to } 0…1 of its height.
  • align?: 'normal' | 'up' — Copies point along the surface's normal (default) or straight up.
  • sink?: number — How much of a copy is pushed into the surface, 0…1 of its height. Default 0.35.
  • jitter?: number — 0…1 randomness in where copies go (grid, spiral, ring).
  • scale?: [number, number] — A random scale for each copy, [min, max].
  • tilt?: number — ring: degrees each copy leans out from upright. Default 45.
  • radius?: number — ring: where the copies' bases sit, 0 (the centre) … 1 (the box's edge). Default 0.
  • within?: 'sphere' | 'cone' | 'box' | 'cylinder' | 'bunch' — volume: the shape they pack: sphere, cone, box, cylinder or bunch (grapes). Default sphere.
  • gap?: number — volume: copies' spacing, 0.5…2 of a copy's size. Default 0.95.
  • spin?: boolean — Random spin of each copy about its own up. Default true.
  • seed?: number — Seeds the random placement, spin and scale of the copies: a whole number from 0 to 9999. Default 1.

ModelScatterItem

interface ModelScatterItem

The copy a scatter makes many of: a shape and its options, sized in meters.

  • shape: ModelShape — Any shape but scatter, coat or none.
  • size: [number, number, number] — One copy's size, meters.
  • shapeOptions?: Record<string, number | boolean> — As ModelPart.shapeOptions, for the copy.
  • profile?: ModelPoint2[] — As ModelPart.profile, for the copy.
  • outline?: ModelPoint2[] — As ModelPart.outline, for the copy.
  • bevel?: number — As ModelPart.bevel, for the copy.
  • path?: ModelPoint3[] — As ModelPart.path, for the copy.
  • sweep?: ModelSweep — As ModelPart.sweep, for the copy.
  • leaf?: ModelLeaf — As ModelPart.leaf, for the copy.
  • deform?: ModelDeform — As ModelPart.deform, for the copy.
  • detail?: ModelDetail — As ModelPart.detail, for the copy.
  • facets?: boolean — As ModelPart.facets, for the copy.
  • rotation?: [number, number, number] — Degrees [yaw, pitch, roll] the copy is turned before it is placed: [0, 0, 90] lays an upright capsule down along the surface (a sprinkle, a grain of rice).

ModelScatterMode

type ModelScatterMode = typeof MODEL_SCATTER_MODES[number]

How a scatter places its copies: one of MODEL_SCATTER_MODES.

ModelShape

type ModelShape = typeof MODEL_SHAPES[number]

One of MODEL_SHAPES.

ModelSlice

interface ModelSlice

A plane cutting a part: across axis at at (−0.5…0.5 of the UNCUT shape), keeping the below (default) or above side; the cut face takes cap. The part's size is its box AFTER the cut.

  • axis: 'x' | 'y' | 'z' — The axis the plane cuts across: x, y or z.
  • at?: number — Default 0, the middle.
  • keep?: 'below' | 'above' — Default below.
  • cap?: string — Material of the cut face (default the part's).

ModelSpawn

interface ModelSpawn

What game.spawn takes for a model: game.spawn({ id: 'hero', model: 'hero', x: 0, y: 0, z: 0, tags: ['hero'] }).

  • model?: string | ModelSpec — A model by the name game.models.define gave it, or a spec itself. The spawned object is the model's root, and h.play('walk') plays a clip.

ModelSpec

interface ModelSpec

A whole model.

  • name: string — What the model is: hero, red-car, oak-tree. Used as the object id prefix.
  • version: 1 — The spec version, for migration. Always 1 today.
  • parts: ModelPart[] — The parts, in build order (parents before children is nice but not required).
  • materials: ModelMaterial[] — The palette.
  • clips: ModelClip[] — The animations.

ModelSweep

interface ModelSweep

A sweep: a section swept along a smooth path (path, control points in the part's box, −0.5…0.5 on each axis).

  • radius: number | number[] — Radius in meters: one value, or one per control point, eased between them.
  • section?: 'round' | number | { star: number; depth: number } | { outline: [number, number][] } — round (default), a number of flat sides (3–12), { star, depth } ridges, or { outline } of [x, z] points.
  • squash?: [number, number] — The section's scale [across, up]: an oval.
  • caps?: 'round' | 'flat' | 'point' | 'none' — How the ends finish: round (default), flat, point or none.
  • closed?: boolean — The path loops back to its start (a pretzel, a ring).
  • twist?: number — Degrees the section turns from start to end.
  • beads?: { count: number; depth: number } — The radius swells and narrows along the path: count beads, depth 0…0.9 (croissant, sausage links).

ModelTrack

interface ModelTrack

One animated channel of one part.

  • part: string — The part's id.
  • channel: ModelChannel — Which channel.
  • keys: ModelKey[] — Keys in time order; between keys the value is interpolated by ease.
  • ease?: ModelEase — Default smooth.

ModelVessel

interface ModelVessel

A vessel: a hollow lathe (profile is the OUTSIDE), with a rim, an inside and optionally its contents.

  • wall: number — Wall thickness, meters.
  • inside?: string — Material of the inside wall and floor (default the part's).
  • fill?: { level: number; material: string; surface?: 'flat' | 'dome' } — The contents: level 0…1 of the inside height, its material, a flat (drink) or dome (heaped) top.

PlayOptions

interface PlayOptions

How a clip plays.

  • loop?: boolean — Override the clip's own loop flag.
  • speed?: number — Playback rate; 1 is real time, 0 holds the pose. Default 1.
  • from?: number — Start at this many seconds in. Default 0.