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 walk through, and what it may stand on. 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.
Solidity, and why a house has an inside#
Solid defaults to false, so a part is scenery unless it says otherwise. The client gives a solid part a real collider and every other part a trigger — still findable by the crosshair, but not something a body stops against.
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 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 client's. The server does not simulate collision and never refuses a position because something is in the way — a wall stops a player because the player's own client stops them. Support is a question about the world; being blocked is a question about the moment, and the browser is the thing with the frame in its hands.
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.
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.
- Overlap the treads a little. Two that meet exactly edge to edge leave a line of no width between them, and a stride that lands on it finds neither. A few centimetres of overlap costs nothing and closes the seam.
- 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 it blocks movement. False unless said |
Path | The line a Ribbon runs along: two to 64 points |
Draped | Whether a ribbon lies on the ground it crosses |
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 prism: ramps, 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 |
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.
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 lands 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.
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.
What this is not: collision between objects. Things fall to the ground, not onto each other, and nothing bounces, rolls or topples. The server still does not simulate collision, and a wall stops a player because the player's own client stops them.
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 | The ability to travel, and the record of a journey in progress | 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, and the fall in progress | Weight. It falls when built in mid-air | yes — 0.1–30 m/s², not already falling |
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#
The Settler is eleven parts — root, torso, head, two arms, two legs, each in two segments — generated 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. A figure the world generates for itself derives its proportions from a seed, so the same seed is the same person in any world.
A design carrying the character tags animates: the client finds the limbs by their tags and walks it.
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 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.