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— Thetoolchildren 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, sogame.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:countlobes,depth0…0.95,sharp0 (waves) … 1 (narrow creases) — pumpkin, pepper, garlic.noise?: { amount: number; scale?: number; seed?: number }— Smooth lumps over the surface:amountin meters,scalethe 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 ofMODEL_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 toparts.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 anoffset. 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… SeeModelPattern. 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, asgeometry-3dtakes 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'ssizesizes 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; anonepart 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 (markedtool) 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.stripesrun along the axis,countof them around it;bandsrun across it, stacked along it;swirlwinds round it (a spiral on a flat face);ringscircle it;segmentsare wedges round it with thin lines between;checker;gridis thin lines;dotsare even polka dots;speckletiny random flecks;sprinklesshort random rods;spotsbig soft blotches;grainwavy lines along the axis (wood).pipsandnumberspaint adie's faces: each face's pips (up to nine, then its number) or its number, in the first color,amounthow bold. On any other shape they draw nothing.colors: string[]— The marks' colors, CSS hex: one to three.speckle,sprinkles,dotsandspotspick among them at random; the rest take turns.scale?: number— World units (meters): one repeat, or one mark's spacing fordots,speckle,sprinklesandspots. 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. Defaulty.angle?: number— Degrees: turns the pattern about its axis. Onbandsit 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, aboutaxis.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, soaxisis not used. A shape whose surface has no length and round (a box) usespart.
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 whenrootis 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 andx,y,z,yawandscalehere are ignored. The model dies with it, androot.play('walk')plays a clip. This is howgame.spawn({ model })(core-3d) builds one. Default: a new root atx, 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; seeMODEL_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,cylinderorbunch(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>— AsModelPart.shapeOptions, for the copy.profile?: ModelPoint2[]— AsModelPart.profile, for the copy.outline?: ModelPoint2[]— AsModelPart.outline, for the copy.bevel?: number— AsModelPart.bevel, for the copy.path?: ModelPoint3[]— AsModelPart.path, for the copy.sweep?: ModelSweep— AsModelPart.sweep, for the copy.leaf?: ModelLeaf— AsModelPart.leaf, for the copy.deform?: ModelDeform— AsModelPart.deform, for the copy.detail?: ModelDetail— AsModelPart.detail, for the copy.facets?: boolean— AsModelPart.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,yorz.at?: number— Default 0, the middle.keep?: 'below' | 'above'— Defaultbelow.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 namegame.models.definegave it, or a spec itself. The spawned object is the model's root, andh.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,pointornone.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:countbeads,depth0…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 byease.ease?: ModelEase— Defaultsmooth.
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:level0…1 of the inside height, itsmaterial, aflat(drink) ordome(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.


