> ## Documentation Index
> Fetch the complete documentation index at: https://docs.summerengine.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Spatial placement skill for AI agents

> Place 3D objects and modular kit pieces from measured geometry: floors, walls, facades, pipes, alcoves. A Summer Engine skill for AI agents.

`spatial-placement` is a skill from the [Summer skill library](/mcp/skills): a guide your AI agent reads when the task matches, so it builds the good version on the first try. Place 3D objects and modular kit pieces from measured geometry: floors, walls, facades, pipes, alcoves.

| | |
| - | - |
| Status | Preview: not yet exercised in-engine by the Summer team |
| Domains | Scenes and project, 3D models, World building, Level design, Verification |
| Version | `1.1.0` |
| Engine version | `>=4.6` |
| Recommended set | Yes, installed by `--recommended` |

## When your agent uses it

* Positioning a prop, character, collider, or platform so it sits flush, avoids overlap, or keeps clearance
* Placing something against a wall, inside an alcove, or on a shelf and needing to know which side is blocked and by how much
* Repairing an overlap or a floating object after a placement
* Post-placement checks on a rotated prop using its own local axes
* Placing modular kit pieces — facade or wall modules edge to edge, storeys stacked, pipes, gutters and lamps on a wall, clamps along a pipe
* Measuring a kit piece's size, origin and which way it faces before placing it
* Checking that a facade line is flush, with no gaps and no module standing proud

## Install

`summer setup` installs every skill for your agent. To add only this one:

```bash theme={null}
npx -y summer-engine@latest skills install spatial-placement --agent claude-code
```

Swap `claude-code` for your agent, such as `codex` or `cursor`. See [Skills](/mcp/skills#quick-start) for every option.

## The skill

Place from measured geometry, never from guesses about size or facing. Use
Starcast as spatial evidence, not as an automatic placement solver. Make a
reasonable transform from scene intent, inspect the result, then correct only
the axes that the evidence shows are wrong.

Every tool below is already shipped. Arguments, result shapes, limits and the
engine ops behind them: [references/kit-placement-tools.md](/skills/spatial-placement/references/kit-placement-tools).

## Choose the tool

| Question | Tool |
| - | - |
| Size, origin, front/back plane, pipe ends of a piece not yet placed? | `summer_inspect_asset` |
| Place one piece at an exact pose | `summer_instantiate_scene` with `position`, `rotation_degrees` |
| Place many pieces, read the receipt | `summer_batch`, placed `InstantiateScene` ops, `receipt: "summary"` |
| Next module edge to edge, next storey on top | `summer_place_adjacent` |
| Pipe, lamp, AC unit or sign on a wall | `summer_attach_to_surface` |
| Clamps, braces, posts or windows along a line | `summer_repeat_along` |
| Pipe or duct end to end | `summer_connect_ports` |
| Surface and normal in front of a point | `summer_raycast` |
| Gap or overlap between two pieces; fronts flush? | `summer_measure` |
| What surrounds a placed piece, which side is blocked? | `summer_starcast` |
| Will this pose fit in a tight spot? | `summer_test_placement` before committing |
| Seat a prop on a floor, table or ledge | `summer_snap_to_surface` |
| Line up or space 2-16 placed pieces on one axis | `summer_align_distribute_3d` |
| Did exactly that change, does it look right? | `summer_world_snapshot` + `summer_snapshot_diff`, `summer_screenshot` |

## Placement loop

1. Call `summer_get_scene_tree` and inspect the subject and intended anchor.
   Never guess paths, transforms, or dimensions.
2. Place the subject approximately with `summer_set_prop` or `summer_batch`.
3. Call `summer_starcast(scenePath, path, detail="summary")`.
4. Correct position or rotation from the smallest relevant set of facts.
5. Call summary again. Stop when the intended support/contact and clearances are
   satisfied. Do not keep recasting a placement that already meets the brief.

Use `detail="full"` only when summary identifies an ambiguous blocker, several
nearby candidates, or a complex overlap. Full output is capped at 12 KB and may
return summary automatically. Keep `directionSpace="world"` unless placement is
defined relative to a rotated subject; then use `directionSpace="local"`.

## Read the result

* `subject.position` and `subject.size` describe the queried object.
* `grounded: true` means downward support was detected. It does not mean the
  object is centered or otherwise well placed.
* `contactStatus` and `contacts` identify touching or overlapping colliders.
  The physics shape query cannot distinguish touching from penetration here.
* `directions.<label>.status` is `blocked` when clearance is below the tool's
  threshold; `distance` is measured from the subject bounds.
* `evidence: "physics"` is exact collider-query evidence.
* `evidence: "visual_aabb"` includes visible meshes without colliders, but is a
  world-axis-aligned broad-phase approximation. Do not infer exact surface
  contact from it.
* Warnings about missing collision shapes or visual bounds mean coverage is
  partial; state that limitation rather than inventing certainty.

## Modular kit placement

1. `summer_inspect_asset` every piece type once and read its `summary` first:
   size, where the origin sits (`origin.label`), the two largest opposite plane
   pairs (a large `+z` plane at the max z offset means the visible face looks
   along +z and the back is `-z`; a `one_sided` sheet with no opposite plane
   is invisible from behind), port-like loops (pipe and duct ends), anchors
   and colliders. Read the kit's own notes too, but trust the
   measurement.
2. `summer_world_snapshot`; keep the `snapshot_id`.
3. Place the first piece of a line with `summer_instantiate_scene` and its
   `position` / `rotation_degrees`. Place the rest relative to it:
   `summer_place_adjacent` along the line, again along `y` for the next storey.
   For many pieces at known poses use one `summer_batch` with
   `receipt: "summary"`.
4. Mount wall pieces with `summer_attach_to_surface`, using the back axis from
   step 1. Instance each at its mount height first: the tool keeps the height
   and pushes the piece's back face onto the wall, and refuses (putting the
   piece back) when the seat lands on another node. Join pipe and duct runs
   with `summer_connect_ports`; its `other_ports` says where a bend's free end
   now points. Add clamps and braces with `summer_repeat_along`.
5. Check: `summer_measure` plane mode on each facade line (every front on one
   plane), pair mode on joints you doubt, `summer_starcast` with
   `directionSpace: "local"` on mounted pieces, `summer_test_placement` before
   committing a piece in a tight spot. Starcast's local names follow the engine:
   `forward` is -Z, `back` is +Z. A piece with a -Z back should read
   `forward` blocked by the wall at about the standoff (or the wall in
   `contacts` when flush) and `back` open.
6. `summer_snapshot_diff` against the id from step 2, then `summer_screenshot`
   and look at it. Fix facing from the measurement, not by eye.

### Worked example: two-storey facade wall with a downpipe and a lamp

Pieces (measured in step 1): `wall_double` is 2 x 3 x 0.2 m, origin bottom
centre, front `+z`; `window_double` the same size; `gutter_section` is 1 m tall
with its back `-z` and open loops at both ends; `wall_lamp` has its back `-z`;
`wall_clamp` is small with its back `-z`. Scene `res://facade_test.tscn` with a
`Facade` Node3D at the origin.

Place every piece at a rough pose in one `summer_batch`, then let the measured
tools seat each one:

```text theme={null}
summer_batch {scenePath:"res://facade_test.tscn", receipt:"summary", ops:[
  {op:"InstantiateScene", parent:"./Facade", scene:"res://kit/wall_double.tscn", name:"G1", position:[0,0,0]},
  {op:"InstantiateScene", parent:"./Facade", scene:"res://kit/window_double.tscn", name:"G2", position:[2,0,0]},
  {op:"InstantiateScene", parent:"./Facade", scene:"res://kit/window_double.tscn", name:"U1", position:[0,3,0]},
  {op:"InstantiateScene", parent:"./Facade", scene:"res://kit/window_double.tscn", name:"U2", position:[2,3,0]},
  {op:"InstantiateScene", parent:"./Facade", scene:"res://kit/gutter_section.tscn", name:"Pipe_1", position:[2.15,0.1,0.5]},
  {op:"InstantiateScene", parent:"./Facade", scene:"res://kit/gutter_section.tscn", name:"Pipe_2", position:[2.15,1.1,0.1]},
  {op:"InstantiateScene", parent:"./Facade", scene:"res://kit/wall_lamp.tscn", name:"Lamp", position:[1,2.6,0.5]}]}
    -> one compact receipt: the seven nodes and their poses

summer_place_adjacent {scenePath:"res://facade_test.tscn", subject:"./Facade/G2",
  reference:"./Facade/G1", axis:"x", side:"max", alignOtherAxes:{y:"min", z:"max"}}
    -> verify {gap:0, residuals:{y:0, z:0}}
summer_place_adjacent {..., subject:"./Facade/U1", reference:"./Facade/G1",
  axis:"y", side:"max", alignOtherAxes:{x:"min", z:"max"}}
summer_place_adjacent {..., subject:"./Facade/U2", reference:"./Facade/U1",
  axis:"x", side:"max", alignOtherAxes:{y:"min", z:"max"}}
summer_measure {scenePath:"res://facade_test.tscn", mode:"plane",
  nodes:["./Facade/G1","./Facade/G2","./Facade/U1","./Facade/U2"], face:"+z"}
    -> coplanar:true, spread 0

summer_raycast {scenePath:"res://facade_test.tscn", origin:[2.15,0.5,1], direction:[0,0,-1]}
    -> hit ./Facade/G2, normal [0,0,1]: the wall front, by physics
summer_attach_to_surface {scenePath:"res://facade_test.tscn", subject:"./Facade/Pipe_1",
  ray:{origin:[2.15,0.5,1], direction:[0,0,-1]}, backAxis:"-z", upAxis:"+y", standoff:0.02}
    -> seated_on "Facade/G2", final_gap 0.02, back_face_gap 0.02; the pipe keeps y 0.1
summer_connect_ports {scenePath:"res://facade_test.tscn", subject:"./Facade/Pipe_2", subjectPort:"-Y",
  target:"./Facade/Pipe_1", targetPort:"+Y"}
    -> tilt_degrees 0, verify {distance:0, angle_degrees:0}, other_ports [{id:"+Y", direction:[0,1,0]}]
summer_repeat_along {scenePath:"res://facade_test.tscn", template:"res://kit/wall_clamp.tscn",
  parent:"./Facade", start:[2.15,0.3,0.02], end:[2.15,1.9,0.02], spacing:0.45}
    -> count 4, created ["Facade/wall_clamp_1", ...]

summer_attach_to_surface {scenePath:"res://facade_test.tscn", subject:"./Facade/Lamp",
  surface:"./Facade/G1", backAxis:"-z", standoff:0}
summer_starcast {scenePath:"res://facade_test.tscn", path:"./Facade/Lamp", directionSpace:"local"}
    -> contacts ["Facade/G1"], forward (-Z, the lamp's back) blocked at 0, back (+Z) open
summer_snapshot_diff {from_id:"<id>"}  then  summer_screenshot
```

Loop ids come from the section's `open_loops` in step 1 (its
`summary.port_like_loops`): `+Y` is the outermost loop facing +Y in the
piece's own axes (here the top end), `-Y` the bottom; a second loop facing the
same way is `+Y#2`. The ids do not depend on radius or pose.
On the target pick the loop whose `direction` points where the run continues;
on the subject, the one that points back at the target. A join that would tilt
the piece more than 5 degrees is refused and lists the ports that fit; pass
`allowTilt: true` only for an intended tilt. With kits that ship Marker3D
anchors, pass their names instead.

## Placement recipes

* **Floor/platform:** require downward support, no unintended contacts, and open
  movement/access directions. Correct vertical position first.
* **Shelf:** require shelf support, no back/side overlap, and enough forward
  clearance to remain visible and reachable.
* **Wall:** use the intended horizontal direction as the anchor, preserve a
  small gap unless contact is requested, and verify the opposite side is open.
* **Alcove:** check back, both sides, up, down, and relevant diagonals. Request
  full only if summary cannot identify which surface blocks the subject.
* **Rotated object:** use local directions only when "front", "side", or "up"
  refers to the object's orientation; use world directions for level axes.
* **Overlap repair:** move along the clearest axis with the shortest correction,
  then rerun. Do not resize the asset unless the user asked for that.
* **Facade line:** modules edge to edge with `summer_place_adjacent`
  (`alignOtherAxes` `{y: "min", z: "max"}` for a +z front), then
  `summer_measure` plane mode on the fronts. A module reported `proud` or
  `recessed` is moved along the face axis by its `deviation`.
* **Pipes and gutters:** back on the wall's front plane (`summer_attach_to_surface`
  with the measured back axis), runs joined with `summer_connect_ports`, clamps
  with `summer_repeat_along`. Starcast in local space: the back-axis direction
  (`forward` for a -Z back) blocked at about the standoff.
* **Wall fixtures (lamps, AC units, signs):** `summer_raycast` to find the
  wall point, `summer_test_placement` if the spot is crowded, then
  `summer_attach_to_surface`.

## Guardrails

* Always provide exact `scenePath` and `path`; selection fallback is not
  deterministic agent behavior.
* Starcast is read-only and never moves or saves the subject.
* One representative ray per direction can miss off-center geometry. Combine
  directional results with contacts, nearby evidence, hierarchy, and rendered
  verification for high-consequence placements.
* Cameras, lights, audio, navigation nodes, scripts, and plain Nodes are not
  obstacles unless they own collision or renderable geometry.
* Prefer two useful summary calls around one correction over repeated full calls.
* Never infer a piece's facing from its file name or a screenshot alone.
  Measure it (`summer_inspect_asset`), place it, then confirm with Starcast
  (local) and a screenshot.
* Batch receipts over a few ops: always `receipt: "summary"`.
* `visual_aabb` measurements (measure, place\_adjacent) are box bounds: an
  overhang or a lip sets the face. Say so when it matters.
* A piece seated flush against a single-sided wall can read `open` in the
  wall's direction while `contacts` lists the wall: Starcast's one ray per
  direction starts at the touching face. Read `contacts` too.

## From a shell

No Summer MCP tools in this session? Run each one as a command; when `summer --version` is missing or below 4.0, use `npx -y summer-engine@latest` in place of `summer`. Fields are flags in kebab-case (`--scene-path res://main.tscn`) or one `--input '<json>'`, and `summer ops describe <op>` prints the exact input. The `summer-cli` skill has the rest.

| In this skill | Command |
| - | - |
| `summer_align_distribute_3d` | `summer engine align-distribute-3d` |
| `summer_attach_to_surface` | `summer engine attach-to-surface` |
| `summer_batch` | `summer engine batch` |
| `summer_connect_ports` | `summer engine connect-ports` |
| `summer_get_scene_tree` | `summer engine get-scene-tree` |
| `summer_inspect_asset` | `summer engine inspect-asset` |
| `summer_inspect_node` | `summer engine inspect-node` |
| `summer_instantiate_scene` | `summer engine instantiate-scene` |
| `summer_measure` | `summer engine measure` |
| `summer_open_scene` | `summer engine open-scene` |
| `summer_place_adjacent` | `summer engine place-adjacent` |
| `summer_raycast` | `summer engine raycast` |
| `summer_repeat_along` | `summer engine repeat-along` |
| `summer_run_script` | `summer engine run-script` |
| `summer_screenshot` | `summer engine screenshot` |
| `summer_set_prop` | `summer engine set-prop` |
| `summer_snap_to_surface` | `summer engine snap-to-surface` |
| `summer_snapshot_diff` | `summer engine snapshot-diff` |
| `summer_starcast` | `summer engine starcast` |
| `summer_test_placement` | `summer engine test-placement` |
| `summer_world_snapshot` | `summer engine world-snapshot` |

## Files in this skill

* [`references/kit-placement-tools.md`](/skills/spatial-placement/references/kit-placement-tools)

## Related skills

* [`world-building-3d`](/skills/world-building-3d): Compose, ground and space 3D scenes with Summer's four spatial tools, one geometric decision at a time.
* [`scene-composition`](/skills/scene-composition): Scene structure: node hierarchy, when to extract sub-scenes, reusable prefabs, instance vs add node.

***

Source: [`library/skills/spatial-placement/SKILL.md`](https://github.com/SummerEngine/summer/blob/main/library/skills/spatial-placement/SKILL.md), rendered from `summer-engine@4.3.0` on npm.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.