Skip to main content
spatial-placement is a skill from the Summer skill library: 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.

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:
Swap claude-code for your agent, such as codex or cursor. See Skills 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.

Choose the tool

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:
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.

Files in this skill

  • world-building-3d: Compose, ground and space 3D scenes with Summer’s four spatial tools, one geometric decision at a time.
  • scene-composition: Scene structure: node hierarchy, when to extract sub-scenes, reusable prefabs, instance vs add node.

Source: library/skills/spatial-placement/SKILL.md, rendered from summer-engine@4.3.0 on npm.