Categories for models. Each pattern is a way of using the nine tools that
survives contact with the live viewport. If status or AGENT_RULES.md
disagrees with a line here, status and the rules win.
Units are metres, radians, and seconds.
When: first message, reconnect, lost context, or after tool_contract_mismatch.
- The native window must already be open. MCP discovers it through the per-user session marker. Do not start authoring against a dead host.
- Call
three_studio_statusbefore anything else. - Treat
capabilities, limits,revision, project id,viewport.viewMode, and the live tool schemas as the only truth for this process. - If a capability is false or missing, say so and build the best honest supported result. Never claim RTX, scripting, physics, export, file import, or gameplay unless status exposes it and the current schema has the operation.
- If the adapter still reports
tool_contract_mismatchafter a refresh, stop and ask the human to reconnect. The window predates this adapter.
Do not infer support from an earlier Studio session, a tutorial module, or
DESIGN.md.
When: starting a build or switching work.
- For a live demonstration,
three_studio_projectcreate a new path with a meaningful name. Opening a leftover project hides the build. - Then inspect the active scene, revision, and only the catalog or resources needed for the next decision.
- Inspect is paginated and hashed. Do not request the whole mesh, whole graph sockets as raw dumps, or unbounded arrays.
- Explicitly use
preset: "summary"plus dottedselectfields for the next decision. Useformat: "rows"for collections andifHashto avoid receiving an unchanged payload again. - Use
sceneDigestfor the tree. UseresourceDigestfor counts, hashes, and references. Loft geometries always returnloft.sectionsidentities (id,index,pointCount,transform,localBounds); requestinclude: ["components"]for control points. Never guess loft section IDs. A missinggeometry.loft.editsection includeserror.data.sectionIds. UsemeshElementswith ameshFilter(bbox, y-range, boundary,notAdjacentTo) instead of paging a cloth. UsemeshSelectionwhen an edit needs all matching indices at once; it supports bounds, radius, boundary, manifold, sharp/crease, material, and face-normal criteria and returns an exactselectionHash. Feed that hash togeometry.selection.editso a spatial or material selection cannot silently drift before mutation. UseoperationCatalogto find the exact typed mutation name,geometryCatalogto inspect supported recipes, defaults, and budgets,lookCatalogfor material-look defaults and raster notes beforematerial.look.create,lightingDigestfor rig and light intensities,plainformCatalogfor migrated controlled-English statement families,plainformAstto inspect a proposed program before apply, andgraphCatalogbefore graph authoring. UsegraphDigestand readsockets(source,compiled,live), notinputs.$summary. - Recolor or retune a semantic look with
material.look.patchon the same material id.material.look.createstays create-only. Optional look scalars (roughness,opacity,transmission, and the rest of the look schema) override the recipe. ReadlookCatalogfirst: default glass transmission is 1, andemissiveLensdefaults to amber#ff3b08. Raster glass islook: "glass"withtransmission: 0and an opacity below 1.camera.framemay target entities created earlier in the same apply; it uses authored recipe bounds when the compiled revision does not yet contain them.view.distanceScalescales camera distance and does not multiplypaddingbelow 1. - Carry
selectionHash, membership hashes, and modifierstackHashinto the apply that needs them. Never bulk-mutate a stale or half-paged selection.
Exact stable IDs from inspect are the only IDs you may write. Never mutate by fuzzy name or tag.
For timed authoring runs, read status.authoringTelemetry. Retained counters
describe the bounded HUD window; cumulative survives HUD pruning and reports
tool totals, authored and lowered operation counts, compile/capture counts,
PNG bytes, and elapsed time. Apply responses also expose redacted operation
families and lowering/kernel/compile/preview timings without IDs or payloads.
When: every mutation.
One apply is one coherent intent: “ground plane and key light”, not “the entire still life”. The human is watching the window. A 40-operation dump looks like a teleport.
Every apply needs:
- latest
baseRevision - a new
idempotencyKey(do not reuse after a timeout) - one human-readable
label - exact IDs (or aliases created in this same changeset)
Aliases exist so you can create a mesh and assign it in one transaction. They are not a second ID namespace across applies.
On success, read diagnostics, invalidation scopes, the new revision, and
pixelForecast. Resolve errors before adding detail.
For a costly batch, dry-run once and retain the returned candidateToken.
Submit the identical operation batch at the same baseRevision with that token
to promote the already compiled candidate. Tokens are content guarded, keep
only one candidate alive, and fail closed after another dry run or project
switch. A successful dry run automatically becomes the visible Preview layer;
the HUD says STUDIO · PREVIEW. Use the Layers tab to inspect the committed
scene, Preview, both, or transient grid/workbench-light helpers. These choices
do not mutate or export project state. The workbench light automatically yields
to authored lighting. Use resource.createMany when provisioning several independent typed
resources so one semantic operation and compact inverse replace many core ops.
Procedural geometry is intentionally concise during block-out. Before detailed
vertex, UV, paint, or topology work, call geometry.realize with the inspected
resourceHash; it atomically replaces the recipe with canonical editable
triangle topology. Loft recipes may use named section descriptors, per-section
TRS, profileResolution resampling, interpolated subdivisions, closest-ring
alignment, and generated side UVs.
Static smooth, simpleDeform, and displace modifiers on editable meshes run
before UV seams are split into render vertices, so one authored vertex cannot
crack into independently smoothed triangle copies.
Compile-heavy tools (apply, render, project, history) have a 120s
budget. Status and inspect stay at 15s. If apply times out, it aborted
before commit. Re-inspect revision. Do not retry the same
idempotency key.
On revision conflict, inspect changedSinceRevision before retrying.
When: a coherent object-layout or shader intent is clearer as bounded English than as a hand-written operation list.
Send Plainform through the ordinary guarded apply envelope:
{
"program": {
"language": "plainform-v1",
"source": "Use entity/window-module as the module.\nLay out a 12 by 30 grid of copies of the module over the front face of entity/tower, spaced 1.5 metres horizontally and 1.2 metres vertically, preserving the prefab orientation."
}
}Plainform is controlled natural English, not unrestricted code. It lowers to
the same typed operations, guards, validation, limits, inverse history, and
candidate compilation as a direct apply. A program accepts at most 256
statements and may generate at most 128 operations. Read the returned
interpretation, lowered operation families, diagnostics, and revision; never
assume an unsupported sentence was approximated.
Before applying unfamiliar Plainform, call three_studio_inspect with
query: "plainformAst" and plainform.source. Typed statements include exact
source spans, semantic keys, and fields. A legacy.statement is still handled
by the compatibility compiler; it does not mean the sentence is invalid. Use
query: "plainformCatalog" with optional plainform.dialect,
plainform.domain, and selector.name filters to discover migrated grammar.
Tokens are omitted from AST inspection unless plainform.includeTokens is
explicitly true.
Sound Plainform begins with Design a sound called … with id audio/… or
Create a sound scene called …. It lowers to an audio graph domain, an
audio resource, a scene with settings.purpose: "sound", and a 3D
visualization (spectrogram heightfield, envelope ribbon, harmonic stacks,
spatial sources). Play bakes the mix to a local WAV and auditions it through
the native HTMLAudioElement; Web Audio is a silent host stub. Render still
captures the visualization. Units include hertz, seconds, beats per minute,
and decibels.
Inspect graphCatalog with selector.kind: "audio" before dense graph edits.
Object Plainform supports exact named entity references, named selections,
spatial relations, transforms, bounded iteration, grouping, prefab creation
and $prefab-name reuse, and face grids. Singular references and selections
can both be transformed, for example Move the tower up by 2 metres and
Rotate each facade panel around y by 0.1 radians. Set independent object
scale axes with Set the scale of the leaf to [1.4, 0.7, 0.25]; inside a
For each block, use Set its scale to [x, y, z]. Every axis must remain
positive. The older ... scale uniformly to ... wording remains supported.
Design Plainform begins with Design a <kind> called <name> with id <stable-id>.
For new spatial or manufactured designs, append using the right-up-forward design frame. That frame is canonical for AI authoring: right is world +X, up
is world +Y, and forward is world +Z. Profiles use [right, up]; loft stations
advance forward/backward; primitive width/height/depth mean right/up/forward.
The compiler owns the internal loft mapping and root transform, so never add a
manual corrective rotation. Continue an existing root with
Continue the design entity/<id> using the right-up-forward design frame
instead of a second Design header. Empty pivot groups and
Put … under …, keeping world pose are the generic rig tools; see
plainform-assembly-action-plan.md.
Headers without the suffix retain the legacy
XZ-profile/Y-loft behavior for compatibility.
Use it for unit-checked parametric solids: named dimensions, bounded integer
loops, trigonometric/easing expressions, exact boxes, spheres, ellipsoids,
capsules, cylinders, and tapered cylinders, arbitrary or symmetric smooth
profiles, independently controlled
loft sections, open guide curves, positional/tangent/curvature interpolation,
bounded local bulge/pinch/offset shaping, bevelled profile extrusion, and
deterministic union/subtract/intersect. For the remaining cross-object surface
attachment case, it also supports exact entity-owned named boundaries,
bounded nearest-surface anchors, constrained open patches between two
$boundary references, optional named end boundaries, and source-surface
tangency derived from anchored normals. These are compile-time authored
constraints rather than a hidden live solver. It emits shared
geometry and batched entities, so repeated mathematical elements do not
consume one operation per mesh. The design root retains the source, evaluated
top-level parameters, and boundary declarations in metadata. Use the full
exact grammar and coordinate conventions in
skills/threebrowser-studio-mcp/references/plainform.md; do not infer
unsupported CAD verbs from open-ended prose.
Rounded primitives use explicit anatomical/manufactured dimensions instead of
post-hoc mesh distortion: spheres take a radius; ellipsoids take width, height,
and depth; capsules take a radius and unambiguous body length; tapered
cylinders take bottom radius, top radius, and height. They accept stable IDs,
centres, rotations or semantic-axis alignment where meaningful, and the same
optional material clause as boxes and cylinders.
Guide binding is explicit when silhouette identity matters. Append following point <one-based-index> of profile <name> to a guide-curve declaration. The
compiler validates the profile and stores the exact zero-based runtime
profileIndex; a guide bound to one profile cannot silently guide another.
Omitting the phrase preserves the compatible nearest-profile-point behavior.
Closed lofts keep their legacy fan caps by default. For deformation-ready end
surfaces, append with <1..32> cap rings before guide and continuity clauses.
Studio duplicates the cap boundary, creates regular concentric rings plus a
centre vertex, budgets the additional topology, and emits planar cap UVs while
preserving longitudinal side UVs.
Surface anchors store triangle/barycentric evidence and, whenever the source
has deterministic coordinates, an interpolated surfaceUv parameter. Derived
indexed deformations preserve these UVs. Sphere, cylinder, loft, hair-card,
fair-union, and ordinary indexed results therefore remain usable by UV-driven
skin/detail materials without an unrelated unwrap step.
Refine an already named deterministic region with:
Subdivide the surface region cheek locally by 2 levels, then relax it for 4 iterations with strength 35 percent.
The compiler selects region-centroid triangles, shares edge midpoints, limits refinement to four levels and 250,000 vertices, preserves UV interpolation, and holds the refinement boundary during bounded Laplacian relaxation.
Intersecting generated parts may request a genuinely evaluated fair union:
Fair Nose Form into Head Shell over $nose-join within 12 millimetres, removing hidden intersecting surfaces, with curvature continuity.
This performs the bounded union, welds coincident output vertices, smooths only inside the named boundary radius with continuity-specific iterations, and generates deterministic planar UVs. It remains a bounded approximation, not a global NURBS solver; the named boundary and radius are mandatory.
Create coordinated gaze without guessing two rotations independently:
Create a coordinated eye pair called Portrait Eyes with id entity/eyes, centred at [0 centimetres right, 3 centimetres up, 8 centimetres forward], separated by 7 centimetres, with eye width 3 centimetres, eye height 1.5 centimetres, and eye depth 1.8 centimetres, looking at [0 centimetres right, 3 centimetres up, 1 metre forward].
This creates a stable group, left/right ellipsoid meshes, and one hidden shared
gaze-target entity referenced by both canonical lookAt constraints.
Hair grooming consumes ordinary reusable guide curves:
Groom a hair card called Fringe with id entity/fringe along guide fringe path, with width 3 centimetres, tapering to 10 percent.Groom a hair strand called Flyaway with id entity/flyaway along guide flyaway path, with radius 0.6 millimetres.
Cards are bounded transported tapered ribbons with deterministic root-to-tip UVs. Strands are bounded six-sided tubes. Both accept the ordinary optional material clause and remain canonical geometry resources.
Design Plainform can also annotate an existing project-owned surface without
creating another mesh. Create a surface curve called ... on ... through surface points nearest to design points ... stores bounded projected anchors
as reusable $curve-name intent. Add closed before surface curve for a
loop. Deterministic surface regions may be named between two same-owner curves,
within an explicit distance of a curve or boundary, inside a closed curve, or
within an explicit radius of one projected surface point. These declarations
store semantic intent on the design root; they do not deform geometry until a
separate supported regional operation consumes them. A vague region such as
Name the region around $rail as shoulder is rejected because it has no
extent.
Curve-distance, surface-radius, between-curves, and enclosed-curve regions can
drive deterministic normal displacement without naming mesh elements. Use
Raise the surface along <curve> by <length> with a smooth falloff of <length>
(also Lower, Inset, Bulge, or Pinch) or <verb> <region> by <length>, falling off smoothly over <length>. Surface-space offset curves create related
lips and bands from anchored normals/tangents. The compiler internally realizes
a bounded indexed result, keeps the owner entity ID and transform, and records
the affected-vertex count as derived evidence. An explicit positive falloff is
mandatory.
Projection can turn a named profile or existing $surface-reference into a
new anchored surface curve on another owner. Profile projection accepts an
explicit design-space centre and optional rotation; reference projection uses
the already evaluated world points. Projection is design intent, not visible
surface detail by itself: a render changes only after a supported deformation,
split, or other geometry operation consumes the projected curve or region.
Shell <owner> inward|outward by <length>
creates a bounded indexed result with duplicated offset skin, reversed inner
winding, and walls on genuine topology boundary edges. Thickness at or above
half the smallest non-zero owner span is rejected conservatively. A requested
open interior boundary is rejected until that curve has become real split
topology.
Design Plainform can feed evaluated surface measurements and reference frames
back into the same typed scope. Use Let <name> be the width|height|depth of <owner>,
Let <name> be the width of <owner> at height <length>, Let <name> be the minimum distance between <owner> and <owner>, or Let <name> be the angle between $curve-a and $curve-b. Let <name> be the point <percent> along $curve, plus tangent/normal/outward variants, supplies bounded vectors for
relational placement and axis alignment. Dimensions and cross-sections are measured in
world space; minimum distance is the deterministic bidirectional
vertex-to-triangle distance between the two evaluated surfaces. Height-specific
cross-sections currently support width only and reject unsupported variants.
Keep <owner> symmetric across its x|y|z centre plane and Maintain at least <length> clearance between <owner> and <owner> persist constraint intent on
the design root and validate before the atomic commit. A later Design
Plainform program revalidates an inherited constraint when it modifies one of
that constraint's participating owners. A violation rejects the whole program
with plainform_constraint_unsatisfied. This is compile-time Design Plainform
enforcement, not a claim that unrelated direct MCP operations participate in a
background global constraint solver.
Intersecting solids generated in the same Design program can be joined with
Attach <tool> to <target> over $boundary, removing hidden intersecting surfaces, with positional continuity. This performs the same bounded,
deterministic CSG topology union as an explicit union while retaining the
semantic attachment reference. Non-intersecting bounds reject. Tangent and
curvature continuity reject until a boundary-aware blend solver can actually
satisfy them; they are never silently relabelled positional unions.
Constraints that participate in a pending CSG or attachment topology change
fail with plainform_constraint_validation_unavailable; author and verify the
topology stage separately because the compiler will not approve a constraint
against the pre-CSG surface and imply it covers the evaluated result.
CSG also enforces hard budgets on intermediate BSP polygon tests, generated
split vertices, and live polygons. A low-triangle curved input can still be
pathological; it now fails with a CSG BSP work or CSG intermediate
diagnostic instead of exhausting the Studio process.
The BSP builder evaluates a bounded set of candidate split planes and prefers
low-split, balanced partitions, allowing small curved attachments to remain
tractable without raising any safety budget.
An existing owner can be divided after authoring with Split <owner> along $closed-curve immediately followed by Call the enclosed surface <name> [with id <stable-id>]. Existing separating topology loops remain exact. When a
projected curve crosses triangle interiors, the compiler performs a bounded
evaluated-topology imprint before producing two non-overlapping indexed
surfaces while preserving the owner ID for the remainder. Imprint records
the intent, Open <owner> along $curve creates a genuine boundary, and shelling may preserve that explicitly
opened reference. Empty/non-separating imprints reject. Surface-space offset
curves plus correspondence-based uniform-clearance constraints cover lips,
seals, and shut lines. Generic transported-frame sweeps, exact centre-plane
mirroring, and source-tangent boundary blends cover repeated manufactured
attachments without subject-specific primitives.
When a complex result is weak, revise the authored profiles, section spacing,
guide binding, dimensions, material, camera, and light before extending the
language. Add a new Plainform/runtime capability only after a required form
cannot reasonably be expressed with the existing general-purpose operations.
Regional deformation moves the evaluated vertices already present in the
source surface. A tiny mask on a coarse four-corner panel may therefore reject
with no affected vertices, while a broad mask can read as an almost uniform
offset. Author enough section/profile resolution for the intended feature;
smooth shading can soften lighting across faces but cannot repair a coarse
silhouette. Prefer named relationships and measured placement over unrelated
guessed world coordinates when parts must remain visually attached.
Remember that controlled sections preserve one base profile's topology: author
silhouette landmarks in that profile, bind important guides to exact one-based
profile points, use separate guides for their longitudinal
paths, and state mirrored local modifiers on both sides. Prove booleans first on
a low-complexity representative part such as a cylinder annulus.
Loft statements accept the same optional using material <material-id> clause
as other generated surfaces, so the primary evaluated form need not depend on
a later non-Plainform assignment.
Face-grid orientation is explicit and independent from placement. Append one of these phrases when the default local-Z-to-face-normal behavior is not the intent:
keeping each copy uprightpreserving the prefab orientationaligning each copy's local x axis with the face normalaligning each copy's local y axis with the face normalaligning each copy's local z axis with the face normal
Shader Plainform begins with Create a shader graph called .... It supports
descriptive feel phrases, typed Principled properties, named math bindings,
and bounded expression chains such as
sin(time * 2 + cos(time * 0.5)), smoothstep, clamp, and saturate.
The compiler validates the generated graph before it can be committed. Follow
the normal graph workflow: inspect graphCatalog, dry-run, validate, then
assign or continue only from the validated result.
Preview these changes and Show me a preview request a dry run in either
dialect. To accept it, resend the identical Plainform source at the same base
revision with the returned candidateToken; the token explicitly promotes the
already compiled candidate and does not trigger another preview compilation.
When: any build the human should follow.
Work in stages the window can show: block-in, primary forms, secondary forms, materials, graphs, lights, dressing, animation, final camera. The subject picks the stages; do not invent a ritual.
For each stage:
- Inspect the slice you need.
- Apply a few related objects or one resource family.
- Validate if graphs or topology changed.
- Render beauty and look at the image.
- Say what you actually saw, then choose the next stage.
Do not add sleep calls. Real model and MCP time is the timing.
Do not translate a finished JavaScript scene, a tutorial module, or
studio-call helper output into one MCP batch. That hides the build and
usually violates IDs, hashes, and catalogs.
The HUD log compact line Apply 30 operations is a count. Expanded details
name whitelisted op types only. Neither is visual proof.
When: deletes, graph resources, large batches, unclear sockets.
Dry-run destructive, large, graph-resource, or high-budget work first.
The compiled candidate is visible in the native window after a successful dry
run even though the canonical revision does not advance. The Preview layer is
removed by promotion, replacement, or project switch. All layers is useful
for spatial comparison, but overlapping unchanged geometry is expected because
the candidate is a complete compiled project rather than a delta mesh.
Every apply (dry-run and commit) returns pixelForecast:
| Forecast | Meaning |
|---|---|
will-move |
Beauty pixels should change |
will-not-move |
Document may patch; 8-bit beauty likely will not |
unknown |
Do not guess |
Catalog-only sockets and bump strength * distance below 1/255 forecast
will-not-move even when the patch succeeds. Trust that over a later
identical PNG. Raise bump distance (metres of height), not only strength.
recalculateNormals on editable mesh is accepted (normals are derived at
compile) and forecasts will-not-move when it is the only edit.
Inspect a subtree and its expected hash before recursive delete. Never delete a referenced resource without reassigning or removing references in the same transaction.
When: placing, parenting, or organising.
- IDs are semantic and stable:
market/stall-03, not runtime UUIDs. - Groups own transforms.
entity.group/entity.ungrouppreserve world TRS when they can. Non-uniform scale that would shear must be restructured or baked on purpose.entity.reparentkeeps the child's local TRS; it does not preserve world pose. For a pivot (axle, knuckle, steering column), author a DesignCreate a group … centred at …or ObjectPut … into a group … centred at [x, y, z], thenPut … under …, keeping world pose. Do not parent animation pivots under a semantic design root unless you have inspected the child's local axes: that root isRx(-π/2), so local Y is world −Z and local Z is world +Y. Nested world-identity groups created by the new assembly sentences store local identity, so knuckle yaw is local Y and wheel spin is local X. - Collections are many-to-many folders. Membership never changes transforms. Deleting a collection never deletes members.
- Prefer
layout.pattern(linear, grid, radial, seeded scatter) when status says it is implemented. Use explicit transforms for everything else. - Prefer
stroke.applyfor authored paths: sculpt a local/world/surface path, paint a color layer or UV data texture, turn the path into a tube, or scatter an existing mesh along it. A single point can stamp; multiple points form a pressure/radius/opacity-varying stroke. Persist commonly reused paths withstoreAsAssetId. - Keep transforms finite and scales non-zero.
- Inspect compiled bounds before placing dependents.
- Use
projectVisibilitybefore editing something that may be off-screen (visible/occluded/background).
When: shaders, texture graphs, Principled, mapped PBR.
inspectquery: "graphCatalog"for the domain (shaderortexture) before every graph pass. The catalog,authoring.canonicalEnvelope, andauthoring.edgePortShapeare the names you may use.- Create graphs as
resource.create/resourceType: "graphs"with the graph nested underresource.graph. Keep envelopeid,name,kind,metadata. - Socket values go in
node.inputs. Node configuration goes inparams. Edges are{ from: { nodeId, port }, to: { nodeId, port } }using catalog port names. three_studio_validateimmediately after every graph create or patch, before assigning the graph to a material.- Patch one socket with
resource.patchnodeInputs. Do not replace a whole graph to change one value.
Live vs catalog: graphDigest.sockets.live === false means the catalog
accepted the value but TSL does not bind it. Principled sheen, specular
IOR/tint, anisotropy, and iridescence compile only when live (weight or
connection). Catalog-only Principled sockets will not move beauty.
sRGB is for display colour (albedo, emissive). No colour space for normals,
roughness, metalness, height, masks. Query
status.capabilities.imageTextures.materialControls for map ranges and
neutral defaults. Raster maps need an active UV layer. The graph image
asset node is CPU-bake only; it is not a live WebGPU texture.
No raw WGSL, GLSL, TSL, or eval through ordinary apply.
Image-based lighting is optional scene-owned intent. Use scene.settings.patch
with patch: { environment: { mode: "studio", intensity: 0.6, rotation: 0 } }
for three broad reflection softboxes, or mode: "sky" for an outdoor gradient.
The runtime deterministically generates a bounded half-float equirectangular
texture and WebGPU evaluates its roughness-filtered reflection environment.
intensity is 0–8 and rotation is radians from −2π through 2π. Optional
skyColor and groundColor are three linear RGB channels from 0 through 1.
Set environment: null to disable. This illuminates standard/physical materials
without changing the background or adding scene objects. Inspect the native
capture before increasing intensity; existing direct lights remain active.
When: looking, framing, claiming a visual result.
status.viewport.viewMode:follow-shotis the authored camera.reviewmeans the human is flying a session-only camera. Evidence,effectiveCamera, andcameraIdstay on the authored shot.camera.framepersists a shot (bounds + presentation aspect) and snaps the window back to Follow shot. Transient render framing is evidence-only.- After a visually meaningful stage,
three_studio_renderand inspect the returned image, not just metadata. - Follow with
beautyDigestfor hashes, clip/black/luma, and(x, y)probes. - Need entity IDs or occlusion?
passes: ["beauty", "objectId"]. Probes then includeentityId. - Current evidence is beauty (plus object-id). Do not request other diagnostic passes unless the live render schema lists them.
capabilities.rtxis adapter support, not activation. Claim RTX lighting only when returned status isactive. Inline raster maps do not appear in RTX hit shading.
Never claim a visual result without a capture from the committed revision.
When: keyframes, Actions, timeline.
Create animation resources through apply, validate them, then
three_studio_play enter / pause / resume / seek / step. Play evaluates
deterministic Action animation and timeline-driven Ocean geometry even when a
scene has no Actions. Ocean is displacement-only: apply it to a sufficiently
subdivided local-XY surface and keep a moving Ocean last among live geometry
modifiers. Dynamic Ocean geometry renders through raster WebGPU and is excluded
from the static RTX triangle scene; set timelineScale: 0 only when a static
Ocean result is intended. Keep the sum of evaluated vertices × waveCount
within capabilities.timelineGeometryMaxSamples across distinct moving oceans.
Typed blueprint controller graphs run when status reports
controllerRuntime: true. Author scene-owned settings.controller with one
exact controlled entity and attach blueprint graph IDs through that entity's
components.logic. Enter (or the configured activation key) begins a
runtime-only session; Escape is globally reserved, clears input, restores the
authored pose/UI/cursor, and returns to Author. Use only the event/action nodes
listed by capabilities.logicRuntime and validate after every graph change.
Entities use a Unity-like typed component model. entity.self supplies the
controlled entity ID; component.has can branch on typed capabilities. Camera
nodes activate, follow, aim, and adjust perspective FOV. rigidBody and
collider components provide bounded fixed-step box, sphere, one-sided ramp, and static mesh physics, triggers,
and collision enter/exit events. Prefer a root entity for a moving rigid body;
Use shape: "ramp", a positive size, and slopeAxis: "x" | "-x" | "z" | "-z"
for authored jump faces. Use shape: "mesh" on a mesh entity to collide against its compiled triangles as a bounded, one-sided static terrain surface. Dynamic mesh colliders, joints, CCD, soft bodies, cloth, fluids, and simulation caches remain unsupported.
For generic surface-following bodies, set rigidBody.alignToSurface: true; tune surfaceAlignSpeed and maxSurfaceTilt to smoothly pitch and roll the body toward the live contact normal while preserving authored yaw control. Do not combine surface alignment with a component that owns pitch and roll; check the component catalog and validation diagnostics for compatible settings.
behaviorRuntime remains false: arbitrary scripts do not run. Unsupported
physics features and uncatalogued blueprint nodes remain unavailable. Do not send script operations
through apply or claim capabilities beyond the live controller contract.
For continuous controllers, query the blueprint catalog for input.keyHeld
(boolean held and numeric value), value.math, value.select,
vector.compose, vector.component, and motion.getSpeed. Scalar math has
finite results bounded to ±1e12; clamp takes value a and bounds b/c,
and invalid division/square roots return zero. Commanded positive speed moves
along local −Z. Use event.onFixedUpdate.delta for integration and persistent
state.get/state.set values for smooth controls. Chain actions deliberately:
a later action reads state and motion values written by the earlier action.
Controller testing uses the existing Play tool. Pause, inject inputAction: "keyDown"
with input: { code: "Enter" }, then inject held keys and step an exact
number of 60 Hz ticks. keyUp takes the same code; releaseKeys clears all
keys and emits release events. Enter retains an existing pause so tests can
start without wall-clock motion. Query Play for controller/physics state,
and inspect exact entities with include: ["transform"] for the canonical
transform alongside the compiled local runtimeTransform. Resume for live
input; injected or native Escape restores the authored state. Controller
capture.hideHud: true also hides the transient authoring grid and restores
its prior visibility on exit.
When: closing a stage or a session.
- Validate after graph, hierarchy, reference, animation, or topology changes, and again before you call the work done.
- Undo / redo are new compensating revisions. Revision numbers never go backwards.
- Save with
three_studio_projectonly after validation and visual review. - Never edit project JSON, the history journal, recovery files, generated assets, or the session marker on disk.
- Never enable trusted-project mode.
Completion means: the project reopens, validation passes, and the last render supports the claim. A successful tool payload is not completion.
These fail in this product even when they work in a Three.js snippet.
| Do not | Do this instead |
|---|---|
Write projects\...\*.json yourself |
three_studio_project + apply |
Run a tutorial .mjs or studio-call to “build the scene” |
MCP tools only |
| One apply with the whole finished scene | Visible stages |
| Guess Blender node / socket / RNA names | graphCatalog |
| Guess operation or geometry recipe fields | operationCatalog / geometryCatalog |
Raw WGSL / GLSL / TSL / eval |
Catalogued graph nodes |
| Reuse an idempotency key after a timeout | Re-inspect, new key |
Bulk patch without selectionHash |
Inspect the exact set first |
| Treat Review fly-cam as the render camera | Authored shot + camera.frame |
| Treat an unchanged PNG as “apply failed” | Read pixelForecast and sockets.live |
| Claim gameplay, import, export, or jobs | Status capabilities |
Put C:\... asset paths in the document |
Bounded dataTexture, or a checksum-verified local sceneImport job that records portable content and provenance |
| Grow an inspector UI | MCP + the live window |
three_studio_job supports bounded textureBake, sceneExport, and
sceneImport actions when reported by status capabilities. Inspect the current
job schema and capability limits before starting. Local GLB import requires an
absolute source path and its expected SHA-256; only portable resources and
filename/hash provenance enter the project. Use the normal dry-run preview and
candidate-promotion flow. See the asset-generation import guide
for the supported subset, budgets, and unsupported features.