Blueprints and procedural assets#
How a machine, a character or a building is described as data — and why nothing in the server knows what any of them are.
The claim#
A blueprint is geometry plus components plus scripts. Put those three together and you have a working machine that nobody wrote a class for. That is the claim, and the whole asset system exists to make it true rather than aspirational:
ScoutBotis nine primitives, four components and a Lua file.- A
Settleris eleven primitives generated from six numbers. - An agent's
SurveyPostis three primitives, designed by something that is not a person.
All three go through the same validator, the same catalogue, the same wire message and the same model builder. None of them has a code path of its own.
What a design looks like#
A model is a list of parts. A part names a shape and a material; it never carries a mesh, a colour, a shader or a texture path. That is what makes a definition safe to accept from a player or a language model: there is nothing in it to execute.
This is the shape the socket takes, as the Visual of a save_blueprint action — MCP's world_create_design and a brain's create_blueprint say the same thing in a friendlier spelling, described further down:
{
"Name": "SurveyPost",
"Parts": [
{
"Id": "base",
"Primitive": "BeveledCube",
"Position": { "X": 0, "Y": 0.1, "Z": 0 },
"Scale": { "X": 0.6, "Y": 0.2, "Z": 0.6 },
"Material": "Stone",
"SemanticTag": "base"
},
{
"Id": "mast",
"Parent": "base",
"Primitive": "Cylinder",
"Position": { "X": 0, "Y": 1.0, "Z": 0 },
"Scale": { "X": 0.12, "Y": 1.8, "Z": 0.12 },
"Material": "Metal",
"SemanticTag": "mast"
}
]
}
Position is metres from the parent's pivot; Scale is the part's size in metres.
The four rules a design can rely on#
- Scale is the size in metres. Every primitive is normalised to a unit cube by the client, including the cylinder, conventionally two units tall.
- Position is an offset from the parent's pivot, in metres, in the parent's own frame. A parent's scale does not cascade: a head 0.35 m above a chassis stays 0.35 m above it whatever size the chassis is. A parent's turn does: the child hangs from the pivot, so turning a wing swings everything in the wing round with it rather than leaving it behind due east.
- A tag is how something finds a part it did not design. The animator locates
upper_leg_leftwithout knowing anything else about the figure. Soliddecides what a body may stand on, and what stops it. A wall is solid; a lamp's glow, a window pane and a robot's antenna are not. Only the design knows what the shape is meant to be, so only the design can say — and the server, not the client, reads it.
Solidity, and why a house has an inside#
Solid defaults to false, so a part is scenery unless it says otherwise. A solid part is something a body stands on and something a body stops against; every other part is a picture, still findable by a press, but a body walks through it and cannot stand on it.
That one flag is what makes interiors possible. Four walls with a gap left for a doorway is a building you can walk into and build inside; without it, a house is a shape you walk through. The town builder's house design is exactly that: floor, three walls, two front piers with a 1.4 m gap between them, a lintel and a roof, all solid, with non-solid window panes set into them.
A solid part fills the space it occupies, and that is easier to forget than it sounds. A glass tower drawn as one solid box the size of a storey is not a glazed room — it is a room nobody can enter, and the difference does not show until somebody tries to be in it. It will still answer the question "what is holding me up?" with the top of that box, so a floor above it reads as walkable while no body can stand there. If you mean a curtain wall, build the wall: panes a hand-width thick around the edge of the storey, with the space between them left empty. If you mean a solid mass — a plinth, a pier, a hillside fort — say so and mean it. Both are legitimate; only one of them has an inside.
Two different things read that flag, and it is worth knowing which is which.
What holds you up is the server's. The server answers the highest solid part-top at or below where you are, and falls back to the terrain where there is none — so a floor slab is something to stand on, a walk into a building ends on the slab rather than in the terrace under it, a crate let go inside lands on the floor, and a gallery over an atrium is a second storey that works. The height a thing can reach up to is half a metre, which is also what makes a step, a staircase and a ramp work without anything knowing what those are.
Three kinds of part are deliberately not consulted, because their upper face is not a level plane and a confident wrong answer is worse than none: anything tipped or laid along a slope, a ribbon, and a round shape. A part merely turned about the up axis is fine, and so is one hanging off something turned that way — a wing set off the street grid has a floor like any other wing. A Prism is consulted, its top being level, but only over its own outline: a body in the notch of an L-shaped building is on the ground, not on a roof that is not there. A part that is turning or sliding at this instant is not consulted either — it is somewhere the design does not describe, and nothing here carries a rider.
What stops you is the server's too. A walk is swept with the walker's own body — from its feet plus the half metre it can step over, up to the top of its head — and the first solid face that swept band would meet ends the walk half a foot short of the face. What is left of the travel then slides along the face, at most twice, so a body walking slantwise into a wall walks along it rather than stopping dead, and a body already against a face with nowhere to slide is refused, naming the face. A teleport into a solid is refused the same way. The same rule runs in the browser, so that it can decide not to ask for a walk the server would refuse; it decides nothing else.
Which shapes stop you follows which shapes hold you up, over the same parts and the same declines: a cube and a bevelled cube at their box, a wedge on its slope and its sides and its back, a prism and a rigid ribbon on the walls of their outlines, and a sphere, cylinder or cone on its true round — the widest section the body's band reaches, not the box around it. Anything tipped over, and a draped ribbon, neither holds anybody up nor stops anybody. A part that is turning or sliding at this instant stops nobody, which is how a door that is swinging open is walked through and a shut one is not. Half a metre is the same number on both questions: a tread half a metre high is stepped on to, and a lintel a person's height up is walked under.
A doorway is a gap between parts. Two piers and a lintel, with nothing between the piers. A hole cannot be cut in one part — a wall is a box and a box has no holes — so a wall with a door in it is always at least two parts, and a door is a third, solid, that the design swings out of the way.
Steps, stairs and ramps#
Half a metre is the whole of it: a body standing on one surface can reach the top of anything up to half a metre above it, and nothing higher. Nothing in the world knows what a staircase is — a flight of steps works because each tread is within reach of the one below, and stops working the moment one is not.
Do not type a flight out tread by tread. Declare one tread and repeat it — the section on repeats below — and every tread is computed from the one before it, exactly. A flight and the floor above it were once written three centimetres apart by hand, and a walker fell through the seam about one crossing in twenty; nobody can see three centimetres in a list of forty parts, and a repeat has no seam to see.
So when you draw a flight, divide the rise by the number of treads and check the answer before you build it. A grand staircase climbing 8.3 m in sixteen treads is 0.52 m a tread, which draws beautifully and admits nobody. The same climb in forty-two treads is 0.20 m, and anyone can walk up it.
Three more things settle whether a flight actually works:
- Land the top tread flush with what it arrives at. A flight that stops short, or climbs past its landing, leaves a drop at the top that no amount of correct tread spacing fixes.
- Meet the treads, or overlap them; never leave a gap. Two that meet exactly edge to edge are fine — a point on the shared edge is answered by both boxes, and by exactly one of two prisms — which is what a
repeatbuilds and what the example below shows. What fails is a gap: a strip between two treads finds neither, and although a walker is stood on a footprint a quarter of a metre long so that a seam narrower than that is crossed, anything wider is a hole somebody meant. A few centimetres of overlap is harmless and costs nothing. - Check what the flight crosses. A tread inside something else is not the surface anybody stands on — the higher one wins — so a stair cut against a terrace or a tier has to stay above it the whole way, or start where that thing ends.
A ramp is the same rule with the steps made small enough to disappear, and needs nothing else said about it.
How tall a design is decides how it meets the hill#
The world has no flat ground. Something has to reconcile a design, which is drawn on level axes, with a slope — and the whole design's height is what picks between three answers. Nothing else is consulted: not the entity type, not the name, not what any part is tagged.
Draped — anything with a draped ribbon in it. The ground is left alone and nothing is turned; each part reads the terrain under its own points. A road built this way runs over a hill the way a road does.
A metre or taller. A pad is cut level under the footprint, ramped out over five metres at its edge so it is a terrace rather than a plinth, and the design stands upright on it. This is a house, a tower, a bridge pier, a hangar.
Shorter than a metre, and not draped. No pad is cut. The whole design is turned to lie along the slope, the way a paving slab sits on a hillside.
That third case is worth knowing about before you meet it, because it is the one that goes wrong quietly. A turned design's parts are no longer upright, and an upper face that is no longer level is one the server declines to answer — so nothing in a tilted design holds anybody up. It looks completely correct and you walk through it. A footpath 0.3 m thick, a car park, a jetty, a helipad: all drawn fine, all scenery.
The fix is never to make it thicker. Drape it. A road, a path, a plaza, a quay — anything whose job is to lie on the ground — is a draped ribbon, and then it follows the hill point by point and keeps its surface.
One consequence of draping surprises people, so it is worth saying plainly: a draped road is not what holds you up. The ground under it is, at the same height, which is why world_route and a body's own standingOn both name the ground while you walk down a street. That is the rule working, not failing. What you stand on is what the road is lying on.
Every field of a part#
| Field | Meaning |
|---|---|
Id | Its name. Lowercase letters, digits and underscore, at most 32 characters, starting with a letter |
Primitive | One of the shapes below |
Parent | The part it hangs from, or nothing for a root part |
Position | Offset from the parent's pivot, in metres, in the parent's own frame |
Rotation | How it is turned, as a quaternion. Identity if not said. Written as a quaternion and authored as three angles: world_create_design, world_preview_design and POST /designs/preview take yaw, pitch and roll in degrees and convert |
Scale | Its size in metres along each axis |
Material | One of the materials below |
Colour | A colour of the design's own, overriding the material. Null means "use the material" |
SemanticTag | What this part is for: head, upper_arm_left, bulb |
Solid | Whether a body stands on it and stops against it. False unless said |
Path | The line a Ribbon runs along, two to 64 points; or the closed outline a Prism stands on, three to 64, the last joining the first |
Draped | Whether a ribbon lies on the ground it crosses. A prism stands, and cannot be |
Repeat | This part declared once and built many times: a count, 2 to 64, and a step in metres. Expanded into ordinary parts the moment a design is read, so a stored or sent design never carries one — see the section on repeats below |
Surface | A pattern out of this design's own Materials, drawn over the colour. Null means a flat surface |
SurfaceTiling | How many times that pattern repeats across a metre. 1.0 unless said |
Turning a part#
A stored part is turned by a quaternion, because that is what interpolates cleanly and what the client's shader wants. Nobody writes one. So the tools a person or a model authors through — world_create_design, world_preview_design and POST /designs/preview — take three angles in degrees instead and convert:
| Field | Turns about | At 90 degrees |
|---|---|---|
yaw | The up axis | The face that pointed east points south |
pitch | The east axis | The face that pointed up points north |
roll | The north axis | The face that pointed east points up |
Each is −360 to 360, they default to zero, and they are applied roll, then pitch, then yaw. A design that says nothing about turning is untouched.
This is what a pitched roof, a diagonal brace, a cable stay, a ramp handrail and a flight of leaning eaves are made of, and it is worth saying plainly that nothing built before this had any of them: the field was in the definition from the start and the authoring vocabulary had no word for it, so every design ever written here was square on to the compass.
Turning is honoured everywhere a design is read. A part's box is the box of its eight turned corners, so the review measures a leaned brace along its own length; the previews draw it turned; and what holds you up still declines to answer for a part tipped off vertical, because its upper face is no longer a level plane.
Shapes and materials#
PrimitiveShape | What it is |
|---|---|
Cube | A box |
BeveledCube | A cube with its edges cut, which reads better at low poly counts |
LowPolySphere | A faceted sphere, deliberately coarse |
Cylinder | A cylinder, normalised so its scale is its size in metres |
Cone | A cone, standing on its base |
Wedge | A triangular block, low at one end and full height at the other: ramps, pitched roofs, blades |
Plane | A flat quad, for panels and signs |
Ribbon | A strip laid along a path: a road, a footpath, a fence line, a canal |
Prism | A closed path extruded upward from its base: a building's walls, its floor and roof, a quay, a raised planter |
AssetMaterial: Grass, ForestGrass, Wood, Leaves, Stone, Water, Metal, White, Black, Red, Blue, Yellow, Orange.
Both are closed sets, listed in full with their stored numbers in the schema reference. A design can only name something the client already knows how to draw — it can never describe geometry the client would have to execute code to produce. A design that wants a colour the palette does not have gives three numbers rather than naming an asset, which is why Colour changes nothing about the sandbox.
Surfaces: a pattern the design draws itself#
The palette is thirteen names and three channels of colour, and neither of them can say "brick". A design may therefore carry its own surface materials: small SVG drawings, at most eight per design and sixteen kilobytes each, that its parts reference and that repeat across a surface a given number of times per metre.
{
"Name": "Garden wall",
"Materials": [
{
"Id": "00000000-0000-0000-0000-000000000001",
"Name": "brick",
"Svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 64 32\"><rect width=\"64\" height=\"32\" fill=\"#6f392c\"/><rect x=\"1\" y=\"1\" width=\"30\" height=\"14\" fill=\"#b07354\"/><rect x=\"33\" y=\"1\" width=\"30\" height=\"14\" fill=\"#b07354\"/></svg>"
}
],
"Parts": [
{
"Id": "wall",
"Primitive": "Cube",
"Scale": { "X": 6, "Y": 3, "Z": 0.3 },
"Colour": { "Red": 1, "Green": 1, "Blue": 1 },
"Surface": "00000000-0000-0000-0000-000000000001",
"SurfaceTiling": 1.4,
"Solid": true
}
]
}
The drawing is multiplied over the part's colour, so a white part shows it exactly and a coloured part shows it tinted. Tiling is per metre, not per part, so the same brick is the same size on a garden wall and on a tower.
A material's identity is a digest of its own bytes and is never chosen: the same drawing written by two people is one material, and storage keeps one copy however many designs use it. Callers do not compute it — state any placeholder id consistently within the design, as above, or over MCP and from Lua name the material by its human name, and the server settles it.
All three authoring doors carry surfaces, and a script's is no softer than the others': a Lua brain writes a materials list of { name, svg } tables and gives a part a surface name and a tiling, and the drawing reaches the catalogue exactly as written, to be held to the allowlist below in the same words as anybody else's. A part naming a drawing its design does not carry — or one two of its drawings share the name of — refuses the design rather than being quietly left unpainted.
This is still not a way to name something that gets loaded. The SVG is held to an allowlist of sixteen elements and the painting attributes that go with them: no script, no style, no image, no use, no text, no foreignObject, no event handlers, no href of any spelling, no DOCTYPE, and no reference outside the drawing except url(#name) into itself. The full list, and the reasoning, is in the schema reference. The shipped Pattern Pavilion is the worked example: ask an agent to build one, or look at it in the design catalogue.
Ribbons: things that run along the ground#
A Ribbon is width (Scale.X), thickness (Scale.Y) and a path. Without a path it runs straight along +Z for Scale.Z metres. Corners are mitred, so a bend is one strip rather than a row of overlapping slabs, and a path is a list of points in the part's own frame — the design says where the bend goes and never describes a triangle.
A ribbon that is Draped lies on the ground it crosses: its heights come from the terrain under each point rather than from the design, which is what makes two draped strips that meet along a line agree there exactly — both are reading the same ground. It keeps its own thickness, and its Position height becomes an offset above the surface. Parts hanging from a draped part ride its surface at their own spot, so a lamp on a footpath stands on the footpath.
Only a ribbon can be draped. Anything else that says so is refused.
Prisms: things that stand on their own outline#
A Prism is a closed path extruded upward. Its Path is the outline it stands on — three to 64 points in the part's own frame, the last joining the first — and Scale.Y is its height in metres. Scale.X and Scale.Z multiply the path rather than sizing the part, so they are 1 for an outline already traced in metres, which is what world_create_design's width and depth default to when left out.
It exists so that a building's floor and roof can follow its walls. A wall is a box per edge of the footprint and always was; a floor or a roof for an L-shaped corner building could only be laid on the footprint's bounding rectangle, which put a roof over the street. A prism is that footprint, once, with the notch left out — and it can be concave: an L, a U, the outer ring of a courtyard block. It may be listed either way round. Which way somebody wrote their corners down is not a mistake, so a clockwise outline is turned round rather than refused.
Its pivot is its base, unlike every other shape, whose pivot is its centre. A building is placed at the ground, so a prism at Y: 0 stands on the floor it is given and rises Scale.Y from there; a second storey's walls sit at the first storey's height, not at half of it. Its top is a level plane, so a body can stand on it — but only inside its outline, as the paragraph on what holds you up says. A prism cannot be draped; it stands. Like the ribbon, it says where the corners go and never describes a triangle: both renderers cut the same outline into the same triangles by the same rule.
An outline is untrusted input and has to be a simple polygon before anything draws it, so four things are refused, each naming the points: a ring that crosses itself — the edge from outline point 0 to 1 crosses the edge from 2 to 3 — a repeated point — outline points 0 and 3 coincide, so the outline touches itself — a point on a straight edge — outline points 0, 1 and 2 lie in a line; drop point 1 — and a ring enclosing nothing — the outline encloses no area. Two points closer than a centimetre are refused as a ribbon's would be, and the check is made on the outline with the part's scale applied, so a scale of a thousandth cannot shrink two distinct points into one it would not see. A prism without a path is refused rather than drawn as a box.
{
"Id": "floor",
"Primitive": "Prism",
"Position": { "X": 0, "Y": 0, "Z": 0 },
"Scale": { "X": 1, "Y": 0.3, "Z": 1 },
"Material": "Stone",
"Solid": true,
"Path": [
{ "X": 0, "Z": 0 }, { "X": 0, "Z": 8 }, { "X": 6, "Z": 8 },
{ "X": 6, "Z": 5 }, { "X": 10, "Z": 5 }, { "X": 10, "Z": 0 }
]
}
That is an L-shaped floor slab, 30 cm thick, ten metres by eight with a four by three notch out of one corner. The walls are the same outline again, hung from the floor at Y: 0.3 with a Scale.Y of the storey height; the roof is the same outline a third time.
Repeats: one part declared, many built#
A flight of stairs, a fence, a colonnade, a row of sleepers: the same part again and again, each one a fixed distance from the last. Repeat says so once. It is a count — how many there are in all, counting the part as written, 2 to 64 — and a step: how far the next copy is from the last, in metres, along each axis.
{
"Id": "tread",
"Primitive": "Cube",
"Position": { "X": 0, "Y": 0.09, "Z": 0.125 },
"Scale": { "X": 1.2, "Y": 0.18, "Z": 0.25 },
"Material": "Stone",
"Solid": true,
"SemanticTag": "step",
"Repeat": { "Count": 12, "Step": { "X": 0, "Y": 0.18, "Z": 0.25 } }
}
That is a flight of twelve treads, each 18 cm up and 25 cm on from the one before, in one declaration. The moment a design is read — through any door, by anyone — the repeat is expanded into ordinary parts: tread_1, tread_2 … tread_12, and the name as written names nothing afterwards. Each copy is the part's own position plus a multiple of the step, one multiplication a copy, so the twelfth tread is exactly where arithmetic says and not where eleven additions drifted to. The back edge of one tread is the front edge of the next to the bit. Everything downstream — what holds you up, what a click picks, what a demolition takes down, what the previews draw — sees twelve treads and nothing else; a stored design and a design on the wire carry plain parts and no repeat at all.
The step is in the parent's frame, the frame Position is written in, so a flight hung from a wing turned off the street grid runs along the wing. A repeat copies everything hanging from the part as well: a step that is a tread with a riser under it is one repeat on the tread, and riser_7 hangs from tread_7. A repeated part inside a repeated part is built first, and its copies carry both numbers — baluster_2_7 is the second baluster on the seventh tread.
To hang something off one copy, name that copy. The name as written stops naming anything the moment the design is read, so a landing whose Parent is tread is not a landing on the flight — it is a thirteenth part of the repeat, copied twelve times along with everything else, and you get twelve landings marching up the stairwell. The top tread is tread_12, and that is what a landing at the top hangs from. The copies are numbered from 1, and the part as written is copy 1, not a separate part beside them.
A repeat fills a run; it does not close a hole. The sides of the opening a flight rises through are parts like any other, and nothing computes them from the flight — a repeat knows the step between its copies and nothing about the floor it passes through. A stairwell whose opening is left unwalled is a hole with a flight in it: you can walk off the side of the treads into it, which is the world being honest about what the design says. Draw the opening's sides, or fit the flight to a well the floor's own parts already enclose.
The reason it exists is not typing. A flight and the floor above it were once written three centimetres apart, by hand, in a file of forty parts; a walker fell through the seam about one crossing in twenty, landed a storey down and walked on underneath the floor it was climbing towards. The same seam, one layer up, drew a walker a storey down for a single frame. A hand-written landing overhung the top treads and made a whole flight unclimbable. When the positions are computed, none of those can be typed.
A repeat is untrusted input like the rest of a design, and one that cannot be made is refused in words naming the part, never half-built: a count under 2 or over 64 — is repeated 0 times; a repeat makes 2 to 64 copies, counting the part itself — a step that is not a number, a step shorter than a centimetre — repeats with a step of 0 m between copies, and copies must be at least 0.01 m apart — a copy whose name another part already has, a copy whose name would pass 32 characters, a repeat that would push the design past 512 parts, and a repeat waiting on a refused repeat inside it. A step rather than a path to lay the copies along, on purpose: everything a repeat is wanted for is a straight run, a flight that turns is two flights with a landing between them, and a spacing divided out of a path's length is a figure nobody checks by eye.
Using a thing, and what it does about it#
A design can say what using it does to its own parts, without a line of script:
"capability": {
"useVerb": "Open",
"whenUsed": [ { "part": "leaf", "turnTo": 82, "turnRate": 140, "solid": false } ],
"andBackAfter": 25
}
Each entry in whenUsed names one of this design's parts and what to do to it, in exactly the words world.setPart takes — solid, visible, colour, axis, turn, turnRate, turnTo, slide, slideRate, slideTo. Using the thing applies them. andBackAfter, in seconds, is how long that stands before those parts go back the way they were, at the same rates; there is no second list to write, because the world remembers what they were. Using it again while the change stands puts them back at once and cancels the return. It needs a useVerb, since being used is the trigger.
That is a door, and it is also a gate, a lid, a hatch, a shutter, a lever, a valve, a turnstile and a drawer, because the world is not told which of those it is — only that using this moves that part. It is a property a thing can have, like falls or swims, and not a kind of thing it can be.
Name the part that moves, not everything on it. Whatever hangs off a part comes with it: turn a leaf and its handle, its hinges, its letter slot and the window set into it all go round with it, because they are its children and the motion is applied where the hierarchy is still live. So a door with nine parts on its leaf is still one entry in whenUsed. The same is true of a part a script moves with world.setPart; this is not new, it is just newly worth knowing, because a declaration is where most people will meet it.
What a moving part does not do is hold you up. A part that is turning or sliding at this instant is not consulted for what you stand on — it is somewhere the design does not describe — which is the rule already stated above and the reason a leaf swinging open does not carry you with it.
It costs one scheduled event while the change stands and none at all once it has gone back, or if it never does: a thing left standing open for a week is one stored row. Anything conditional — only its owner may open it, only if unlocked, only at night — is a program and wants a script; this is for the unconditional case, which is almost all of them.
Weight#
A design that wants to fall says so, in the same place it would ask for an inventory or a computer:
components = {
physics = { gravityMetersPerSecondSquared = 9.81 },
}
Anything with that component falls when it is built in mid-air, and comes down on the ground beneath it. Anything without it stays exactly where it was put — which is what keeps a lamp on a table, a road on a hillside and a house's own walls from sliding down.
Being let go and being thrown are the same thing to this world, and a thing dropped straight down arrives at a surface exactly as a thrown one arrives at the face it meets. So a ball let go from a shelf bounces by its bounce below, just as a ball thrown at the floor does; there is no second kind of ball, and nothing gains or loses a property by the way it set off.
Gravity is a number, not a switch, so a design can ask for the moon's 1.62 and take two and a half times as long to land. Nothing in the engine assumes Earth.
There is no physics tick, and there must not be one: a fall is a parabola, so the server solves the landing moment in closed form the instant something is let go and schedules a single event for it. A fall of ten kilometres costs one event; a thousand simultaneous falls cost a thousand events in total; a world of resting objects costs nothing at all. The client draws the arc between from the same numbers, without being sent a single update.
A thing with weight also falls off a ledge: a walk whose ground drops away by more than a step is walked to the brink, level, and ends there in the air, and the fall takes over — to the surface under it, one event — while a thing without weight slides down the line as it always did. The shipped Settler has weight, so a person walks off a ledge and falls, and falls with a floor that is taken down, and lands. Nothing yet happens to a person for landing; there is no damage in this world.
Open water is not a ledge. Walk at a canal and you are walked to the quay and left standing on it, dry: where a brink has open water under it, the last dry point behind you is where you stop, and no fall begins. Falling in would otherwise mean standing on the canal bed, which is not what a bank is for. The same holds wherever a thing would be put down over water. The one case with no answer is a thing already over water when the floor beneath it is taken down — there is no dry point behind it to go back to, so it falls where it is.
That is what the water does to something it will not carry. What it will carry is the next two capabilities.
floats — open water holds it up. A thing that floats comes to rest on the surface of a canal or the sea rather than sinking to the bed, and an arc that reaches the water ends there: water is not a face to come off, so a ball that goes in stops, whatever its bounce says, and does not roll on the bed. The shipped Ball floats. Needs falls — nothing without weight was going to sink in the first place.
swims — it makes its own way through open water, and implies floats. Something that swims may be walked into and across a canal, and travels over it at the surface; the walk is planned over a world whose water has a top, so every question it asks is the ordinary one — what holds you up, how steep the way is, what is in the way — of a surface that now includes the water. The shipped Settler swims, so a person crosses a gracht instead of being stopped at it. Step off a quay standing a metre above the water and the ground still drops away by more than a step, so the walk ends at the brink and you go in and surface, which is what stepping off a quay is.
A swim is slower than a walk. A journey with any open water in it is swum from end to end, at swimSpeed — metres a second, needs swims, and left out it is 45% of walkSpeed, which for the shipped Settler is 0.63 m/s against a walking 1.4. One pace for the whole journey rather than a change of pace part way along: that is what lets where a thing is at any moment be worked out from the moment alone, and it means a crossing that is part quay and part canal is swum throughout — the slower of the two, so nobody walks a canal. Swimming belongs to the journey and not to the body, so the same person swims one crossing and walks away from it at their own pace afterwards.
Water is one rule at every door. Whatever cannot ride it is refused a walk into it and refused a teleport into it, in the same sentence; whatever can is allowed both, and arrives on the surface. Building is refused either way — water is not ground to put a house on — and a body cannot be stood up in water from nothing, because taking a body is not swimming into one.
What is approximated, plainly: there is no depth and no draught, so a thing rides with its origin exactly on the waterline whatever its size; a swimmer holds one pace from bank to bank, so there is no wading in and no push off the side; there is no current, no tide and no wave; nothing tires, so a swim has no length it cannot manage; and nothing drowns, because nothing here can.
A thing with weight can also be thrown. throw_entity, world_throw and world.throw give it a velocity, and the world follows the arc against the same solid faces a walk is stopped by — floors, walls, slopes, roofs, the round shapes on their true round — and finds the first one it meets. That moment is the one event; the physics component carries where it set off, when, its Velocity, its gravity and the DurationSeconds the world found, so every browser draws the identical arc and stops it at the identical point. Where it meets something is decided there: the speed into the face is reflected out and scaled by the design's bounce — Restitution on the component, 0 to 1, and 0 unless said. A crate, at 0, comes down on to a floor and rests, and meets a wall and drops down it; the shipped Ball, at 0.6, comes back off a wall and bounces on a floor in ever smaller arcs until what is left into the face is under half a metre a second, and rests. The bounce belongs to the thing and not to what it hits: a ball bounces the same off stone and off grass, because a surface has no restitution of its own yet. A thing is a point along the ground for this against the world's faces — its body's height counts, its width does not. Against other things it is its reach: moving things with weight meet each other when they come within the sum of how far each reaches from its origin, two in flight exchanging their speed along the line between them as equal masses, a ball bouncing off a walker or a resting crate as off a wall, and two people who walk into each other stopping where they meet. Nothing here has a mass, so nothing at rest is shoved, and a walker with no weight is still walked through.
A thing with weight can also roll, if its design says so with rolls — RollingResistance on the component, 0 to 1, how much of its weight the ground drags it with; leave it out and the thing stops where it lands, as a crate does. The shipped Ball rolls at 0.05. A rolling thing is a walk with an acceleration: one straight leg along the surface at a time, its speed changing at the slope's pull less the drag, its height wherever the surface is, one event per leg. A leg ends where the slope changes materially, where a face stands across the way — it bounces off by its bounce — where the ground drops away — it flies off with the speed it has — or where it slows under half a metre a second and rests. What is approximated: the thing is a point on the surface with its body's height and no radius, its speed on a slope is a point mass's rather than a sphere's, and its drag is one number whatever it rolls on.
A thing with weight can grip what it meets, with friction — Friction on the component, 0 to 1, 0 unless said; the shipped Ball is 0.4. At an impact the speed along the face is cut by an impulse against the slip of the contact — at most the friction times the push of the bounce, at most what stops the slip, which for a solid sphere is two sevenths of it — and the cut turns the thing, by five halves of the cut over its reach, about the axis across the contact. That turn is Spin, constant between events; a rolling ball spins at its speed over its reach; a spinning ball thrown at a wall kicks off it. What is not simulated, plainly: no torque between events, no lift on a spinning ball in flight, no drag on spin in the air, no gyroscopic steadiness, one friction whatever the face is made of, and every thing a solid sphere for the turning.
A blueprint — what a design does#
A blueprint is the design with what it does attached, and this is the whole of what a client sends to save one — with the parts, written as above, inside Visual:
{
"type": "action_request",
"RequestId": "…",
"Action": {
"action": "save_blueprint",
"Name": "ScoutBot",
"EntityType": "robot",
"Visual": { "Name": "ScoutBot", "Parts": [] },
"Components": {
"movement": "{ \"MaxSpeedMetersPerSecond\": 1.2 }",
"sensor": "{ \"RangeMeters\": 32, \"MaxResults\": 16 }",
"computer": "{ \"StorageBytes\": 65536 }"
},
"Scripts": { "/home/agent/forest_worker.lua": "…" }
}
}
Components is what makes it work rather than decorative: a movement component makes it able to travel, a sensor lets it perceive, a computer gives it a filesystem to run Lua on. Building one turns them into an ordinary entity, and nothing downstream knows it came from a design.
A new version is a new blueprint, never an edit. An entity built from version 1 keeps meaning what it meant. A revision is a fresh id, the version number one higher, and Supersedes naming the design it replaced. Nothing else changes — the old design stays exactly as it was, and so does everything already standing that was built from it. Building the new id is how you get the new one; demolishing and rebuilding is how one already-built thing moves across.
The components a design can ask for#
Named exactly as they appear in a design and in an entity snapshot on the wire. Every one of these is data: a design asks for a capability, and the server's systems do the rest.
The Author column says whether a design somebody wrote may declare it. An author is untrusted whoever they are — a person, a Lua script, a language model — so the allowlist and its bounds are enforced at the one place a design enters the catalogue, and every door onto it meets them: world_create_design, a browser's save_blueprint, an agent's create_blueprint. The world's own shipped designs are held to the same figures. Out of range is refused, never clamped.
| Name | Fields | Gives it | Author |
|---|---|---|---|
transform | Position, Rotation, Scale | Where it is. Everything has one | no — set when it is built |
visual | VisualKey, Variant, Blueprint | What to draw it as | no — the builder fills it in from the parts |
movement | MaxSpeedMetersPerSecond, Destination, DepartedAt, Origin, Waypoints, InitialSpeedMetersPerSecond, AccelerationMetersPerSecondSquared, OnlyRolls | The ability to travel, and the record of a journey in progress — the leg being walked and the legs after it when a walk slid along a wall, or the speed profile of a leg being rolled | yes — speed only, 0.05–8 m/s |
computer | CpuInstructionsPerSecond, MemoryBytes, StorageBytes, IdlePowerW | A filesystem and the ability to run Lua | yes — ≤ 1,000,000 ins/s, ≤ 64 MiB memory, ≤ 16 MiB storage |
sensor | RangeMeters, MaxResults | sensor.scan from a script, bounded by these | yes — ≤ 128 m, ≤ MaxQueryResults results |
inventory | MaxSlots, Items | Something to hold items, and something to trade with | yes — ≤ 64 slots, ≤ 100 of any item, ≤ 500 in all |
interactable | Verb, Hint, RangeMeters | The word the crosshair shows: Open, Trade, Read, and how close somebody has to be to say it | yes — ≤ 8 m |
physics | GravityMetersPerSecondSquared, Restitution, RollingResistance, Friction, Spin, and the arc in progress: FallingFrom, FellAt, Velocity, RestingHeightMeters, DurationSeconds | Weight. It falls when built in mid-air, can be thrown, bounces by its restitution, rolls by its resistance, grips and spins by its friction | yes — 0.1–30 m/s², restitution, resistance and friction 0–1, not already falling or spinning |
energy_storage | CapacityWh, EnergyAtReferenceWh, ReferenceTime, NetPowerW | A battery, computed analytically rather than stepped | yes — ≤ 100,000 Wh, at rest and undated |
energy_producer | OutputW, IsEnabled | Generation | yes — none, or 0.01–5,000 W |
energy_consumer | DemandW, IsEnabled | Draw | yes — none, or 0.01–5,000 W |
growth | PlantedAt, biomass, rate, WaterFactor | Something that grows over simulated time | no — it is a clock, and a design has no moment of its own |
water | StoredLitres, CapacityLitres | A tank | no — it only feeds growth |
health | Current, Max | Condition | no — nothing yet deals damage |
ownership | Grants | Permissions its owner has given other owners | no — permission is not an author's to grant |
script | ScriptPath, AutoStart | Which program it runs, and whether to restart it | yes — must name one of this design's own Scripts |
group | Root, Tag | Membership of a composite thing | no — the builder sets it |
part_state | Parts | What has been done to its parts: turned, slid, hidden, recoloured | no — a history a new thing does not have |
readouts | Readouts | Readings drawn above it by every client: a bar, a line of text | yes — at most 4, and only bound to a number the design itself may declare |
The exact figures, and the words each refusal uses, are in the schema reference.
Several of the floors are there for a different reason than the ceilings. The scheduler turns these numbers into a single predicted moment — when the journey ends, when the battery runs flat — and a speed or a draw near zero turns that moment into a length of time that does not exist. A design could otherwise stop the simulation rather than merely lie to it.
interactable is a label, not a permission: use works with or without one, and carrying one grants nobody anything. It exists because a client cannot tell that a door is a door.
RangeMeters is the exception, and it is a rule rather than a decoration: somebody else's thing is used from within that many metres of it. A design may ask for less than the world's arm's length and never for more, so the number can tighten the rule and cannot loosen it — a stall that says 5 means "come to the counter", and a thing that declares no interactable at all is held to the 8 m ceiling like everything else. Your own things are exempt, for the same reason your own crates are exempt from a transfer's reach.
Why a design may arrive stocked#
inventory is the one refusal that went the other way. A design that arrives holding goods makes them out of nothing, once per copy, for as long as anybody keeps building it — and it is allowed anyway, bounded, because nothing else in this world makes goods. Refusing it outright would mean nothing could ever hold anything and the shipped town's market stall would be an empty counter. So the bound is on how much rather than on whether: a morning's stock, not a warehouse. When something can actually manufacture an item, that is where the rule tightens.
Designs made of designs#
A design can include other designs. Somebody designs a door; somebody else designs a house that includes it; a third person designs a street of those houses. None of them has to know how a door is made, and improving the door improves every house that includes it — because a house stores the door's identity, not a copy of its geometry.
| Field of an include | Meaning |
|---|---|
Blueprint | The design to include |
Offset | Where it sits relative to the design including it, in metres |
Rotation | How it is turned |
Tag | What the piece is to the whole: front_door, left_wheel |
An included design becomes its own entity rather than being merged into the parent's shape. That is the whole point: a door you can open has to be something use can be aimed at. What holds the group together is a group component naming the root.
A design that includes nothing is not composite, and most are not.
Validation#
Validation reports every problem, not the first, because the author may be a language model that needs the whole list to correct itself. Refusals are values; nothing throws.
The limits, from AssetLimits: at most 512 parts, 128 m along any one axis of any one shape, and a parent chain no deeper than 8. How much room the whole thing may take up is bounded separately for each direction, and measured where a part ends up rather than where it was written: 64 m sideways from the model origin, so a design is at most one 128 m chunk across, 1,024 m up and 64 m down. A design of 512 parts is the same cost to draw and to stand on whatever those metres are, which is why the count is the bound that did not move.
Height is cheap because nothing in this world is indexed by it: chunks, interest management, the entity search and the surface query are all grids in X and Z. Width is not cheap for the same reason, so something wider than a chunk — a hangar, a terminal, anything covering several hundred metres a side — is built as a composite: one design including others, each piece an entity in its own chunk. An include may sit 256 m from the design including it, a design may include 16, and one composite may be at most 256 entities.
Every limit, with the exact wording of each refusal, is in the schema reference.
A definition is checked for: a name, at least one part and at most the limit, unique part ids, a parent that exists, no cycles, finite positions and scales, a known shape and material, and a semantic tag of sensible length.
Four worked examples#
Every world starts with a few designs under fixed identities, so restarting a world re-seeds nothing and every world has the same ones. They are worked examples as much as equipment; /designs draws them.
ScoutBot#
The server ships it under a derived identity, so every world has one and restarting re-seeds nothing.
The Lift, which is not a lift#
The one that is worth reading twice, because it is the answer to "how do I make the world interactive" and there is no Lift anything anywhere in the server.
Six parts: a floor, three walls, a roof and a button panel. The floor is the only one marked Solid, which is what makes standing in the car mean standing in the car. Three components — a computer to run its Lua, movement so it can travel, and an interactable saying that using it is called Call the lift. One script, /lift.lua, and a script component naming it with autoStart, so a freshly built one answers its own button rather than waiting to be programmed.
The Lua is the whole of what makes it a lift:
events.on('use', function(who)
world.ask(who, 'Which floor?', choices())
end)
events.on('answer', function(who, option)
local floor = floorNamed(option)
if floor == nil then return end -- 'exit': you step out
local here = world.position()
world.move(here.x, base() + floor.level * 3.0, here.z)
world.carry(who) -- and you ride with it
end)
Change the words and the arithmetic and the same four things are a vending machine, a ticket barrier, a keypad or a quiz. Nothing in the server learns a new noun for any of them. The full file is scripts/lua/lift.lua; what a question may say and who may be asked one is in PROTOCOL.md.
It travels at 1.4 m/s on purpose — a walking pace, the same as the shipped settler — because a passenger carried along gets a journey of their own at their own speed, and a car that moved faster would arrive without them. That is a decision belonging to this design and not a rule in the world.
A character#
A character is a design like any other. There is no character class, no rig and no model format: the Settler is a blueprint whose parts are the same primitives a house is made of, and it validates, stores, crosses the wire and draws through the paths every other design uses. Anything you can build, you can build a person out of — and the 512-part budget is the same one too.
The shipped Settler is 59 parts: a pelvis and belt, a torso with a collar and buttons, a neck, a head with hair and eyes, two arms and two legs each in two segments, hands, feet with soles, and a ball at every shoulder, elbow, wrist, hip, knee and ankle. Those balls are worth explaining: a part is rigid and turns about a pivot, so a bare hinge shows its corner when a limb swings. A sphere at the joint turns that from a fault into a shoulder.
Everything is derived from six proportions that are ratios of one height rather than measurements: how tall, how much of that is leg, how much is head, how wide the shoulders, how long the arms, how heavily built. The schema reference lists them with their ranges. Every value is clamped rather than refused, so one bad number costs a slightly odd-looking person rather than the whole design, and a figure the world generates for itself derives its proportions from a seed — the same seed is the same person in any world.
What makes a design walk#
Nothing about the Settler in particular. The client finds a figure's limbs by the semantic tags on its parts, so a design written by hand or invented by an agent walks exactly as the shipped one does, whatever it is made of:
| Tag | What it is |
|---|---|
root | the pelvis: what the rest hangs from |
torso, neck, head | the spine, upward |
upper_arm_left, lower_arm_left, hand_left | and the same three on the right |
upper_leg_left, lower_leg_left, foot_left | and the same three on the right |
Only root, torso, head and the four upper limb segments are needed for a design to count as a character. Everything else is taken if it is there and skipped if it is not, so a figure of eleven parts and one of sixty both walk — the second just has more to move.
One convention makes it work: a segment's pivot is its top. Each limb part must be placed so that the centre of its top face is the joint it hangs from — the shoulder for an upper arm, the elbow for a forearm, the wrist for a hand, the ankle for a foot. Put a part half its own length below its joint and that is what you get. Break it and the limb swings about the wrong point.
Whatever hangs off a tagged segment comes with it. A sleeve, a cuff, a bracelet, a joint ball — anything parented to a limb, however deep, swings when that limb swings, and it needs no tag of its own to do so. That is what lets a character be detailed at all: only twelve parts are named, and the other forty-seven are carried.
What the walk does, all of it derived from how far the figure has actually walked rather than from a clock — so a walker off the edge of the screen costs nothing and one that comes back is mid-stride where it should be:
- the thighs and upper arms swing, arms opposing the leg on their own side;
- the knee folds while the foot is in the air and straightens under load, and the elbow keeps a softer, steadier bend;
- the hips rise and fall by exactly the amount a swung leg is shorter than a straight one, so the planted foot never floats;
- the ankle takes most of the leg's turn back out, so the sole stays near level instead of pointing wherever the shin points;
- the shoulders turn against the hips, and the neck holds the head against that turn so a walker keeps looking where it is going.
The stride length is the figure's own, worked out from its leg length, so a taller character covers more ground per step and nobody's feet skate.
Where the ceiling is#
Parts are rigid: they hinge at a pivot and do not bend or stretch, so a figure is a well-jointed one rather than a deforming one. There is no mesh format and no skinning — a design describes what a thing is, which is what lets the server judge what is solid, what holds you up and what a click picked, and what lets something that has never seen a picture invent a house. Detail comes from more parts and from surfaces, not from imported geometry.
A building#
The simplest useful case, and what the surveyor agent designs for itself: a base, a mast and a lamp, of type building, no components and no scripts — three parts, and the only thing that makes it a building rather than a robot is its type.
A town#
What the town builder authors when asked for a city: a house with walls, a roof, a door and windows whose size and colour come from the plot; a road segment with a centre line; a street lamp; a market stall with a computer and stock so you can trade with it.
None of them is a special kind of entity. A type is a validated string, so "house" and "road" are kinds nobody wrote down — which is the whole of the claim this page opened with, and the reason there is no house-building routine anywhere in the server.
Storing, fetching and building#
| Operation | How |
|---|---|
| Save | save_blueprint over the socket, world_create_design over MCP, or create_blueprint from a brain — validated, then written immediately. Once: the same design described again is answered with the one you have, and a different one under a name you already use is refused |
| List | blueprint_list_request → summaries: id, name, version, creator, part count, kind, how many versions the line has — one per line, its latest version |
| Load | blueprint_request → the whole design, any version |
| Spawn | spawn_blueprint action, or world.create from Lua. A design included in another is built at the latest version of its line |
| Change | save_blueprint with Revises, world_update_design, or a brain re-sending a design under the same key. In place — same id, version one higher — while nothing is built from it; a new design with a new id once something is, and the old one is untouched |
| Delete | delete_blueprint or world_delete_design, for a design of yours nothing is built from and no design includes; never a shipped one |
| Tidy | world_tidy_designs: what is duplicated among yours, and with apply the collapse of it |
Whether a change lands in place or as a new version is decided by one fact the server holds and the caller does not: whether anything stored was built from the design. The answer says which happened — outcome over MCP, Design.Outcome on the socket's action_result — and which id now stands. Things built from an older version keep it, and that version stays exactly as it was, so a built thing can never be left without the design it is drawn from; deleting a design something is built from is refused rather than worked around. The catalogue shows one entry per line, the latest, with the older versions reachable from it.
Designs are world knowledge, not private property: anyone who can see a machine may read the design it was built from. Building from one is a different matter — that needs Build permission on the ground it will stand on.
Designs are fetched on demand rather than pushed with every snapshot: a client needs a given design once however many machines are built from it, and most clients will never see most designs.
Who may author one#
Four doors, one road. Every one of them ends at the same place, which validates the geometry, binds the surface materials and holds what the design claims to the allowlist above. A design is untrusted content whoever wrote it, and there being exactly one place that decides is what makes it safe to accept one from a language model.
| Source | How | Held to the allowlist |
|---|---|---|
| Shipped with the server | The ScoutBot, the Settler, the Crate, the Ball, the Pavilion and the Lift, under derived identities so restarting re-seeds nothing | No — compiled in, not sent. A test holds them to it anyway |
| A person, over the socket | save_blueprint, with components as name → JSON | Yes |
| A person, over MCP | world_create_design, with a capability vocabulary instead of raw JSON | Yes |
| An agent | The create_blueprint action, which is how the town builder authors a road, a lamp, a market and its houses | Yes |
The client says what the thing is. Identity, creator, version and the moment of authorship are the server's to assign: a client that could choose its own id could overwrite somebody else's design, and one that could choose its creator could author in their name.
Over MCP the capability is a named vocabulary — walkSpeed, sensorRange, inventorySlots, batteryWh, powerDrawW, computer, falls, useVerb, runScript, shows — rather than component names and JSON. A vocabulary can only say what it has words for, so there is no spelling of it that produces a component an author may not declare, and every bound is published in the tool's own schema so an author learns the ceiling from the description rather than from a refusal. A word the vocabulary has no meaning for is refused by name, listing the words it could have been, rather than dropped: an author who writes walkspeed for walkSpeed should be told, not handed a design that stands still. The same holds for an unknown field in a part, a material, a script, a stock entry or a shows entry.
What a design shows above itself#
shows gives anything built from the design a reading hung over it, drawn by every client: a bar, or a line of text. "This machine shows its battery" is then part of the plan, so ten of them show it and nobody programs one.
"shows": [
{ "key": "battery", "kind": "bar", "source": "energy", "label": "charge" },
{ "key": "what", "text": "beacon", "order": 1 }
]
source is where the number comes from. energy reads the thing's own battery, which costs nothing to keep true because every client is already sent it — and a design binding to a battery it has not declared is refused, since a bar that could never move is worse than no bar. health and growth are refused for the same reasons the components themselves are: nothing yet deals damage, and a plan cannot know the moment a thing was planted.
given is a number of the design's own, and it stands still. A number that changes with time belongs to the built thing rather than to the plan — a design is authored once and built whenever somebody builds it, so a countdown written into one would be counted from a clock that never ran. A script sets one going afterwards, with world.setReadout.
Looking at a design#
A design authored blind is a design nobody sees until it is standing somewhere, which is how a ScoutBot's head floated two and a half centimetres above its chassis for as long as the ScoutBot existed. So the server draws them.
| Where | What it gives |
|---|---|
/designs | Every design in the world, as thumbnails, newest first |
/designs/{id} | One design: an isometric view, four elevations, a review and every part |
/designs/{id}.json | The same, for something that cannot look at a picture |
/designs/{id}/{view}.png | One drawing. iso, front, right, left, top; ?size=thumb for the small one |
/designs/{id}/visual.json | The design as geometry, drawings and all, for something that is going to draw it |
The {id} is the design's identity in lower case.
The five views are not decoration. The isometric is for recognising the thing; the four elevations are for measuring it, because a roof that hovers, a wall that does not reach its floor and a door too short to walk through are all obvious square on and all invisible in a three-quarter view.
Patterns in the pictures#
The pictures the server draws cannot show a surface pattern. The server owns no SVG rasteriser on purpose — a drawing is player-written and the one thing that may render it is a browser, through an <img> — so its drawings paint a surfaced part in the colour that stands for its pattern, read out of the drawing's own fills.
So the pages draw them twice. The server's PNG arrives first and is what a reader with scripting off, and an agent handed the URL, gets. Then, for a design that actually carries a drawing, the browser fetches /designs/{id}/visual.json and redraws the same five views with the patterns in them, using the same placement, the same shapes and the same surface rasteriser the world view uses. Nothing is ever inlined into the page: the drawings arrive as JSON strings and go straight into an <img> as a data: URL.
Each design's page also lists its Surfaces — every drawing it carries, the colour that stands for it, its identity, its size, and which parts wear it at what tiling. The identity is a digest of the drawing's own bytes, so two designs showing the same one are built from the same brick.
The review#
Every design is checked by arithmetic and the findings are shown with it. This is not validation and has no authority: it refuses nothing, because a design is allowed to be strange. It is advice, and every check exists because that fault has actually happened in this world.
| Code | Says |
|---|---|
vanishing | A part too small in some direction to really be there |
enormous | A part more likely a typo than a plan |
crack | Two parts that nearly meet, with a gap too small to be a space |
buried | A part inside another part |
detached | A group of parts, often one, that never reaches the main body |
protrudes | A part coming up through something meant to cover it |
headroom | A part tagged door or gate shorter than the 1.9 m a person needs |
hovers | A design that floats above its own origin |
limb_missing | A figure tagged as a character with a limb absent |
Each finding carries a severity — fault for almost certainly not what the author meant, note for worth knowing and possibly deliberate — the parts involved, the measurement in metres, and the same thing in words.
It does not claim to be complete: rotated parts, round shapes and anything whose correctness depends on intent are outside what arithmetic can see. That is what the elevations are for.
The JSON#
/designs/{id}.json is the half an agent can use. It carries every part as authored and as placed — the offsets the author wrote, and the absolute centre and bounding box each one actually ends up at once its parents are applied — plus the includes, the materials, the review findings, and the URLs of the five drawings.
A part says what it is painted with: surface is the name of one of the design's drawings, or null, and tiling is its repeats per metre, or null on a part with no surface — null rather than 1.0, because a number there would read as a fact about a part that has none. The drawings themselves are not repeated here: materials gives each one's name, its identity, the colour it stands for and its size in bytes, which is what a reader reasoning about a design needs. Something that is going to paint with one asks /designs/{id}/visual.json.
Resolved placement is there on purpose. The authored numbers are relative to a parent and the mistakes are absolute: "the wall top is at 2.80 and the roof surface above it is at 2.77" is a sentence about placed parts, and making every reader recompute that from offsets is how the fault stayed hidden the first three times. An agent that has just authored a design can read this back and correct itself with nobody in the loop.
It is deliberately a different shape from the wire message that builds a machine: a description for something reasoning about a design, not the message a client is sent to draw one.
Drawing one yourself#
If you are writing a client, a design flattens into instanced shapes: an unscaled pivot per part, carrying the semantic tag, with a scaled shape drawn at it. A pivot sits at its parent's pivot plus its own Position turned by the parent's Rotation — the parent's scale is never applied to it — and the server's own placement arithmetic, which is what decides what you can stand on, says exactly the same thing. A part naming a parent that does not exist should be skipped and reported rather than dropped onto the root, where it would silently render a limb in the wrong place; the design was validated on the server, and a client still does not trust it.
A machine whose design has not arrived yet is drawn as a placeholder and rebuilt when the definition lands.