Shapes and modelling with an agent
An agent joined to your session (Build with an AI agent) can do more than place boxes and spheres. Nine tools let it change an object's shape in place, turn a profile into a vase, sweep a rail along a path, cut a sign out of an outline, build a mesh from raw data, copy or mirror an object, bring in a glTF / GLB / OBJ file, write 3D text and model a mesh the way Edit Mesh does. The in-app Assistant has the same tools.
Every call works like the agent's other tools:
- You see the change at once, and so does everyone in the session.
- One call is one undo step. Ctrl+Z takes it back, or press Undo on the call's line in the agent's activity log.
- Viewers cannot use them. A view-only agent gets a refusal and nothing changes.
- A tool that creates something returns the new object's uuid.
Building a model part by part
New in 1.59. An agent can build a model the way you would: a group for the whole thing, and parts made inside it.
- Inside a group, everything is the group's own space. With
parentUuid, a part'sposition,rotationandscaleand its own points (a lathe profile, an outline, a tube's path, a mesh's vertices) are measured in the group, the numberslist_sceneshows for its children. Nopositionputs the part at the group's origin. So a part lands where it was asked even in a group that was moved or turned, and it turns with the group afterwards. (Before 1.59, a part made in a moved or turned group ended up in the wrong place.) - Up to 50 shapes in one step with
create_shapes(below): one call, one undo step. - Colour as you make it:
create_meshtakes a colour per vertex or a material per face, and a flat low-poly look (below). - Shadows: every shape tool takes
castShadowandreceiveShadow(both on by default). - A group that is not there is an error (since 1.61): a
parentUuidthat names nothing makes nothing, and the error says so. Before, the part was made at the middle of the scene with a warning. Any object can hold parts, a restored group included; a light or a camera cannot. - Tiny parts (since 1.61): every primitive goes down to 1 mm (
0.001). - Copies and mirror images with
duplicate(below), and model files withimport_shape(below).
Reference
set_geometry: reshape in place
Changes the shape of objects that already exist. They keep their uuid, position, material and flows, so nothing that points at them breaks. The agent does not delete the object and make it again.
| field | what it does |
|---|---|
updates |
up to 100 objects, [{uuid, …}], one undo step for all of them |
params |
the shape's own settings, the same ones the Inspector's Geometry rows show and list_scene reports: width/height/depth for a box, radiusTop/radiusBottom/height/radialSegments for a cylinder, segments/phiLength for a lathe, depth/bevel for an extrusion. Sizes are capped at 1 km and segment counts to the Inspector's range. |
type |
changes the primitive type in place, for example a box into a Cylinder. params apply on top of the new type's defaults. |
path, radius, radii, closed, radialSegments |
tubes from create_tube: a new path, one radius or one per point, a loop |
force |
needed for an object whose vertices were edited by hand (Edit Mesh, sculpting or edit_mesh). Rebuilding from params replaces those edits, so without force: true the tool refuses and says why. |
create_lathe: turn a profile
Spins a 2D profile around the Y axis, the way a lathe or a potter's wheel does: vases, bowls, bottles, columns, lamp shades.
| field | what it does |
|---|---|
profile |
[[radius, height], …] from bottom to top, 2 to 200 points, in metres. Start and end at radius 0 to close the bottom and the top. |
segments |
steps around the axis, 3 to 128 (default 12; 32 or more looks round) |
phiStart, phiLength |
a partial turn, in radians (default a full turn) |
name, position, rotation, scale, color, physics |
as in create_objects (physics defaults to static) |
parentUuid |
a group to make it in: the position, rotation, scale and the profile are then in the group's own space |
castShadow, receiveShadow |
shadows, on by default |
The profile stays editable: the Inspector shows it as a lathe, and set_geometry changes segments or
phiLength later.
create_tube: sweep along a path
A smooth tube through a list of points: rails, pipes, cables, handrails, tentacles. It is a spline tube, so you can also edit its points in the app afterwards.
| field | what it does |
|---|---|
path |
[[x, y, z], …], 2 to 200 points, in scene space — or in the group's own space with parentUuid. The object is placed at the path's centre. |
radius |
one radius for every point (default 0.05 m) |
radii |
one radius per point, for a tube that tapers |
closed |
joins the ends into a loop (3 or more points) |
radialSegments |
3 to 32 (default 8) |
name, color, physics |
as in create_objects |
parentUuid |
a group to make it in: the path is then in the group's own space |
castShadow, receiveShadow |
shadows, on by default |
extrude_shape: a solid from an outline
A flat outline pushed out into a solid: signs, letters, logos, floor plans, gears, cookies.
| field | what it does |
|---|---|
points |
the outline, [[x, y], …] with x to the right and y up, 3 to 500 points |
holes |
up to 20 outlines cut through it: a window, the inside of an O |
depth |
thickness along +Z, in metres (default 0.2). The solid is centred on it, so it stands upright facing +Z. To lay it flat, rotate it by [-1.5708, 0, 0]. |
bevel |
true, or {thickness, size, segments}, to round the edges |
curveSegments |
1 to 32 |
name, position, rotation, scale, color, physics |
as in create_objects |
parentUuid |
a group to make it in: the position, rotation, scale and the outline are then in the group's own space |
castShadow, receiveShadow |
shadows, on by default |
create_mesh: a mesh from raw data
For shapes that none of the other tools make.
| field | what it does |
|---|---|
vertices |
[[x, y, z], …], up to 20,000 |
faces |
[[i, j, k] or [i, j, k, l], …], up to 20,000. These are indices into vertices, counter-clockwise as seen from outside. A face can have up to 32 corners and is fanned from its first one, so keep faces convex. An index that does not exist is refused with the face's number. |
uvs |
optional, one [u, v] per vertex |
smooth |
smooth shading (the normals are always computed) |
flatShading |
true for hard facets: one flat normal per triangle, the low-poly look |
materials |
up to 16 material slots, each {color, roughness, metalness, opacity, emissive, emissiveIntensity, name} |
faceMaterials |
the slot of each face (0 = the first), one per face in the order of faces: parts in different colours |
colors |
one colour per vertex, in the order of vertices: [r, g, b] (0 to 1) or "#rrggbb". A painted gradient. Since 1.61 the numbers are sRGB, like a colour picker and material colours, and with colors and no color the material is white, so the vertex colours show as given (before, a random pastel tinted them). |
colorSpace |
"srgb" (default) or "linear", for [r, g, b] numbers that are already linear. Hex is always sRGB. |
name, position, rotation, scale, color, physics |
as in create_objects |
parentUuid |
a group to make it in: the position, rotation, scale and the vertices are then in the group's own space |
castShadow, receiveShadow |
shadows, on by default |
subdivide |
1 or 2 smooths the mesh (Catmull-Clark subdivision, since 1.60): corners round off, so a box of six quads becomes a rounded pebble and a few quads become a soft, organic shape. Each level makes about four times the faces, and the result must still fit the limits. Smooth shading comes with it unless smooth: false. |
The reply says how many triangles were made. A quad face stays one face in Edit Mesh, so the mesh is ready
for edit_mesh. Material slots last through later edit_mesh calls; vertex colours last only while an edit
keeps the number of vertices (an extrude, for example, drops them).
update_mesh: new data for a mesh (since 1.60)
Changes a mesh create_mesh made, keeping its uuid, its place and its materials. The whole call is one undo step.
| field | what it does |
|---|---|
uuid |
the mesh |
mode |
replace: every vertex and face again, with the same fields and limits as create_mesh (uvs, colors, faceMaterials, subdivide) · patch: new positions for some vertices, keeping the faces |
offset |
patch: the first vertex index to replace; vertices[0] lands there |
vertices |
the new positions |
uvs, colors |
patch: new values from offset on, as for vertices |
smooth, subdivide |
as in create_mesh (a patch keeps the mesh's subdivision) |
A patch needs the vertex list the mesh was made from. That list is kept in the tab that made the mesh, so after a
reload, or for a mesh made somewhere else, use replace. Primitives change with set_geometry, and any mesh can be
modelled with edit_mesh.
create_shapes: many shapes in one step
Up to 50 shapes of any of the four kinds in one call and one undo step — the parts of a chair, a row of columns, a whole little building.
| field | what it does |
|---|---|
shapes |
[{action, …}, …], 1 to 50 entries. action is lathe, tube, extrude or mesh, and the entry carries that tool's fields under the same name: {"action": "mesh", "mesh": {…create_mesh's fields…}}. parentUuid works in each entry. |
Every entry is checked before anything is made: if one is wrong, nothing is made and the error names it
(shapes[3]: …). The uuids come back in the order given.
A list that does not fit (colors, vertices, uvs) is refused with how many entries arrived and the index
of the first bad one, for example colors[5] (the first bad one of 8 entries): … got [2,0,0].
duplicate: copies and mirror images
New in 1.61. Copies an object with everything in it — children, materials, behaviour, physics — the way Duplicate in the object menu does, so nothing is sent again. One undo step.
| field | what it does |
|---|---|
uuid |
the object to copy |
count |
1 to 50 copies (default 1) |
offset |
[x, y, z] metres between copies, in the parent's space (default: a row to the side) |
mirror |
"x", "y" or "z": the copy is the mirror image across that plane through the parent's centre. In a group, that is the group's centre line, so a left ear becomes the right one; at the top of the scene it is the world's. The triangles are turned round so the copy still faces outward. A box, or a round shape with an even number of segments, is its own mirror image and keeps its editable size. |
parentUuid |
a group (or any object) for the copies; they keep their place in the world |
name |
the copies' name, numbered when there are several |
For tens or hundreds of copies spread over the ground, use scatter with source (Worlds).
import_shape: a glTF, GLB or OBJ file
New in 1.61. Brings in a model made elsewhere: glTF 2.0 (a .glb, or a .gltf with everything embedded) or a
Wavefront .obj. One undo step; everyone sees it.
| field | what it does |
|---|---|
data |
the file as base64 |
chunk |
{id, index, total} for a big file sent in parts, each a multiple of 4 base64 characters. The model is placed when the last part arrives. |
url |
an https address instead (25 MB at most). Local and private addresses are refused, and the server must allow cross-origin reads. |
name, position, rotation, scale, parentUuid |
as in create_objects |
folder |
the Explorer folder (default Imported models) |
castShadow, receiveShadow |
for every mesh in it |
The file goes into your library first (shared with the session), so the scene never needs the address again. Its
named parts (glTF nodes, OBJ o / g groups) become named children, and the reply lists them with their uuids,
so animate_parts or update_objects can move a lid or a wheel.
The app reads what the file is from its bytes, not its name. A model is data: nothing in it runs. A file that points
at other files (an external buffer or texture), or at anything but an embedded PNG / JPEG / WebP image, is refused. An
.obj's .mtl material library is not inside it, so its parts get plain materials; colour them with set_material.
create_text: 3D text (since 1.61)
A sign, a title or a label. Over the connector this is create_shape {action: "text"}. The record travels and is saved, and every screen builds the letters from it. See 3D Text.
| field | what it does |
|---|---|
text |
the words; \n starts a new line |
font |
a bundled font (sans, serif, mono, rounded, display, or its name), any Google Fonts family name (fetched once and kept in the Library), or {asset: "<hash>"} for a Library font |
size |
letter height in metres |
solid, depth, bevel |
solid letters; giving depth or bevel also makes them solid. bevel is a width in metres, false for none, or {size, thickness, segments} |
align, maxWidth, anchor, lineHeight, letterSpacing |
the layout of the block |
color, sideColor, emissive, metalness, roughness, quality, outline |
the look |
position, rotation, name |
where it goes |
To change a text, call set_geometry (edit_shape {action: "params"}) with the same fields under params. list_fonts (assets {action: "fonts"}) lists the fonts you can use. Solid text holds up to 300 characters, or 80 on a phone or headset. A text is made one per call; create_shapes does not take text.
edit_mesh: model a mesh
Runs Edit Mesh operations on a selection of a mesh's faces or vertices.
| field | what it does |
|---|---|
object |
the mesh's uuid |
select |
which part, in the object's own (local) space. mode is faces (default) or vertices. Then any of: indices (numbers from a preview), normal (up, down, +x, -x, +z, -z or [x, y, z], within angle degrees, default 30), box ({min, max}), all: true. Filters combine: normal: "up" with a box means the up-facing faces inside the box. |
ops |
up to 20 operations, run in order. Each one can have its own select. |
Face operations: extrude {distance}, inset {amount 0-1}, move {distance} along the normal,
bevel {width, segments, profile}, subdivide, delete, flip.
Vertex operations: weld (merge into their centre), move {offset: [x, y, z]},
smooth {factor, iterations}, dissolve, bevel {width}, delete, connect (2 vertices),
create_face (3 or 4 vertices).
An operation without its own select goes on with what the step before it left selected, as in Edit Mesh:
after extrude, move or flip that is the same faces, and after inset it is the inner face. After
subdivide, bevel, delete or a vertex operation, the top-level select is read again on the mesh as it
is at that step, and so is any select you give the operation itself. Indices always refer to the mesh as
it is at that step.
Preview first. With ops: [] the tool changes nothing and lists the selected faces or vertices (their
index, normal and centre) plus the totals. Use the preview to check a selection or to pick indices. A
preview leaves no line in the activity log.
After an edit the object counts as hand-edited, so set_geometry needs force: true for it (see above).
If one operation fails, the ones before it stay applied, and the reply says which step stopped and why.
Cookbook
A vase on a lathe
"Put a terracotta vase on the table."
The agent reads the table's position with list_scene, then makes the vase in one call:
create_lathe {
"name": "Vase",
"profile": [[0, 0], [0.12, 0.01], [0.16, 0.12], [0.07, 0.3], [0.09, 0.36], [0, 0.36]],
"segments": 48,
"position": [0, 0.75, 0],
"color": "#c06030"
}
Each profile point is a radius at a height. This one goes from a closed base out to the belly, in to the
neck, out to a lip, and back to the axis. For a wider belly later, the agent sends a new profile: the
Inspector shows it as a lathe, so you can change segments there too.
A rail along a path
"Add a handrail up the stairs."
The agent reads the steps, then sweeps one tube through points about 0.9 m above the nosing of each step:
create_tube {
"name": "Handrail",
"path": [[1.1, 0.9, 0], [1.1, 1.2, 0.6], [1.1, 1.5, 1.2], [1.1, 1.8, 1.8]],
"radius": 0.025,
"color": "#8a6a48"
}
radii with one value per point makes a tapering cable or tentacle. A later
set_geometry {"updates": [{"uuid": "…", "path": [...]}]} moves the rail without making a new object.
An extruded sign
"Make a sign with a window in it over the door."
extrude_shape {
"name": "Sign",
"points": [[-1, -0.4], [1, -0.4], [1, 0.4], [-1, 0.4]],
"holes": [[[-0.8, -0.2], [-0.5, -0.2], [-0.5, 0.2], [-0.8, 0.2]]],
"depth": 0.08,
"bevel": {"thickness": 0.01, "size": 0.01, "segments": 2},
"position": [0, 2.6, 0.1]
}
The sign stands upright facing +Z. Rotate it by [-1.5708, 0, 0] to lay it flat, for example for a floor
plan.
A tower from a box (edit_mesh)
"Turn that box into a tower with a smaller top."
edit_mesh {"object": "…", "select": {"normal": "up"}, "ops": []}
edit_mesh {
"object": "…",
"select": {"normal": "up"},
"ops": [
{"op": "extrude", "distance": 1},
{"op": "inset", "amount": 0.3},
{"op": "extrude", "distance": 0.5}
]
}
The first call is the preview: one face, pointing up. The second call raises the top, insets it and raises
only the inner part again, all in one undo step. Each step carries on with the previous step's selection,
so the last extrude leaves the ring around the inner face where it is. To raise the ring as well, give that
step its own "select": {"normal": "up"}.
A lamp, part by part, inside a group
"Build a little lamp on the table."
The agent makes an empty group on the table with create_objects, then the parts inside it in one step. Every number
is in the group's own space, so the lamp is right however the group is placed or turned:
create_shapes {
"shapes": [
{"action": "lathe", "lathe": {"name": "Base", "parentUuid": "…", "profile": [[0, 0], [0.12, 0], [0.1, 0.03], [0, 0.03]], "segments": 32}},
{"action": "tube", "tube": {"name": "Stem", "parentUuid": "…", "path": [[0, 0.03, 0], [0, 0.3, 0], [0.08, 0.42, 0]], "radius": 0.01}},
{"action": "lathe", "lathe": {"name": "Shade", "parentUuid": "…", "position": [0.08, 0.38, 0], "profile": [[0.03, 0.08], [0.1, 0], [0.11, 0]], "segments": 32, "castShadow": false}}
]
}
One undo step takes the whole lamp back. To swing the shade, create_pivot with the stem and the shade at a point
local to the group (see Undo, pivots and images).