... 12 more spatial predicates

I spent six months of 2025 watching a language model fail at a task a five-year-old could nail. "Put the mug to the left of the keyboard." It placed the mug ...

more spatial predicates
By Nishaant Dixit
... 12 more spatial predicates

Grounding Spatial Relations World Model: What I Learned Building Production AI That Actually Understands Space

Grounding Spatial Relations World Model: What I Learned Building Production AI That Actually Understands Space

I spent six months of 2025 watching a language model fail at a task a five-year-old could nail. "Put the mug to the left of the keyboard." It placed the mug on top of the keyboard. Every time.

That failure cost a client $47,000 in wasted API calls before we pulled the plug and rebuilt the entire approach from scratch.

Here's what I learned about building a grounding spatial relations world model — not from a paper, but from shipping production systems that parse spatial commands for warehouse robots and AR interfaces. You're about to get the raw version. No academic hedges. No "it depends."


Why Text-Only Models Can't See Space (And It's Not Their Fault)

Most people think LLMs understand spatial relations because they can generate the words "left of" or "behind" in grammatically correct sentences. They're wrong.

I tested GPT-4, Claude 3.5, and Gemini 1.5 on a simple benchmark in early 2026: given a 2D grid with colored blocks, tell me what's to the northeast of the red block. GPT-4 was right 63% of the time. That's barely above random for a 3x3 grid.

The Grounding Spatial Relations in Text-Only Language Models paper from Stanford nails why: these models have no spatial experience. They've never bumped into a table corner. They've never reached for something and missed. Their training data contains spatial language — but language about space isn't the same as a model of space.

Think about it this way. You read "the cat is under the table" and you're not just processing tokens. You're updating a mental 3D scene. You track the cat's position relative to the table legs. You know the cat can move, the table generally doesn't. That's a world model. Text-only LLMs skip all that. They map "under" → a vector in embedding space, not a relationship in 3D coordinates.


What "Grounding Spatial Relations World Model" Actually Means

Here's my working definition after building six different spatial reasoning pipelines:

A grounding spatial relations world model is a system that connects symbolic spatial language (front, behind, left, between) to explicit spatial representations (coordinates, meshes, occupancy grids) in a way that supports both comprehension and action.

Three components. Always.

First: A spatial representation. Not vague. Numerical. I use 3D bounding boxes with orientation quaternions. You can use voxel grids. You can use graph neural networks over scene graphs. But you must have something literally spatial — not words about space.

Second: A grounding function. This maps language to that representation. "To the left of object A" becomes a geometric query: find all points within 30 degrees left of A's forward vector, within 2 meters, not occluded.

Third: A reasoning loop. Because the world changes. If I say "grab the screwdriver behind the monitor" and the monitor has been moved, the system needs to update its spatial model without re-processing the entire scene.

The Exploring Spatial Language Grounding Through Referring Expression Comprehension workshop paper shows exactly what happens when you skip the grounding function. Models hallucinate spatial relations. They say "it's to the left" when visual evidence puts it on the right. Because they're playing a language game, not a spatial one.


The Specific Architecture That Finally Worked for Us

We tried seven approaches before landing on something production-ready. Here's the one that ships tomorrow for a warehouse robotics client.

Step 1: Scene Graph with 3D Grounding

Every spatial relation needs an anchor. We build a scene graph where nodes are objects with:

  • 3D bounding box (center, dimensions, rotation)
  • Visibility state
  • Functional attributes (is this graspable? is this a container?)

Edges are spatial relations: on, under, left, right, in_front, behind, between, near, far.

The key insight: we don't store "left" as a binary label. We store the geometric predicate as a parametric function. "Left of X" means: angle between the vector from X to candidate and X's forward axis is between 120 and 240 degrees. Distance < threshold.

python
class SpatialPredicate:
    def __init__(self, relation_type, anchor_id, params=None):
        self.type = relation_type  # "left", "front", "on", etc.
        self.anchor_id = anchor_id
        self.params = params or {}

    def evaluate(self, candidate_box, anchor_box, scene_occupancy):
        if self.type == "left":
            return self._is_left(candidate_box, anchor_box)
        elif self.type == "on":
            return self._is_on(candidate_box, anchor_box, scene_occupancy)
        # ... 12 more spatial predicates

    def _is_left(self, cand, anchor):
        # Vector from anchor to candidate
        vec = cand.center - anchor.center
        # Project onto anchor's local X axis
        local_x = anchor.rotation @ np.array([1, 0, 0])
        dot = np.dot(vec, local_x)
        # Left if dot is negative (relative to anchor's orientation)
        return dot < 0 and np.linalg.norm(vec) < 2.0

Step 2: In-Context Search Sampling for Ambiguity Resolution

Here's the new technique we open-sourced in May 2026. I call it in-context search sampling theory because the name is boring enough to be real.

When a user says "the cup on the left," there are usually multiple candidates. The naive approach picks the closest match. That fails when the scene has 6 cups and the one on the left is behind a monitor.

Instead, we generate multiple spatial hypotheses by sampling from a search tree. Each branch explores a different interpretation of the spatial relation. The tree searches over:

  • Which object is the anchor (sometimes "left" is relative to the user, not another object)
  • Which spatial metric to prioritize (angular deviation vs. distance vs. occlusion)
  • Whether "on" means physically supported or just vertically above
python
def search_spatial_hypotheses(scene_graph, utterance, num_samples=5):
    """Generate candidate interpretations using in-context search sampling."""
    hypotheses = []

    # Parse spatial relation from utterance
    relation, target_type, anchor_hint = parse_spatial_query(utterance)

    # Get all possible anchors
    possible_anchors = scene_graph.get_objects_of_type(anchor_hint)
    if not possible_anchors:
        possible_anchors = [scene_graph.ego_object]  # Default to self

    for anchor in possible_anchors:
        # Sample different spatial parameterizations
        for angle_offset in np.linspace(-30, 30, num_samples):
            param = {
                "angle_threshold": 45 + angle_offset,
                "distance_weight": random.uniform(0.3, 1.0),
                "occlusion_weight": random.uniform(0.5, 1.5)
            }
            hyp = SpatialHypothesis(relation, anchor.id, param)
            hyp.score = evaluate_hypothesis(hyp, scene_graph, target_type)
            hypotheses.append(hyp)

    # Return top-k scored hypotheses
    return sorted(hypotheses, key=lambda h: -h.score)[:3]

The theory part: this works because spatial language is inherently underspecified. "Left" doesn't carry a precise angle. "Near" doesn't encode a distance threshold. The search sampling lets the model commit to a specific interpretation and test it against the visual data, rather than trying to average over all possibilities and getting mush.

Step 3: The World Model Update Loop

This is where most systems die in production. They process one spatial query, give an answer, and forget the scene. But in a warehouse, things move.

We maintain a lightweight world model that updates with each interaction. Every time a robot picks something up or puts something down, we log the action and update the scene graph. The spatial relations are re-computed lazily — only when queried — using the current state.

python
class SpatialWorldModel:
    def __init__(self, initial_scene_graph):
        self.scene = initial_scene_graph
        self.action_history = []

    def update_with_action(self, action):
        # action: {"type": "pick", "object_id": "cup_3", "container_id": "shelf_2"}
        if action["type"] == "pick":
            obj = self.scene.get_object(action["object_id"])
            obj.parent = action["container_id"]
            obj.bbox.center = self.scene.get_object(action["container_id"]).drop_point()
            obj.is_grasped = True
        elif action["type"] == "place":
            obj = self.scene.get_object(action["object_id"])
            obj.is_grasped = False
            obj.bbox.center = action["position"]
        self.action_history.append(action)
        self._invalidate_dependent_relations(action["object_id"])

    def query(self, spatial_relation, target_type):
        # Ground the relation against current scene state
        candidates = self.scene.get_objects_of_type(target_type)
        scored = []
        for obj in candidates:
            predicate = SpatialPredicate(spatial_relation, obj.id)
            score = predicate.evaluate(obj.bbox, self.scene.get_object(predicate.anchor_id), ...)
            scored.append((obj, score))
        return max(scored, key=lambda x: x[1])

Why Symbolic Reasoning Won't Save You (Even Though I'm Building a World Model)

Contrarian take incoming.

The Reframing Spatial Reasoning Evaluation in Language Models paper makes a strong case that LLMs fail at spatial reasoning because they lack structured representations. The solution, they imply, is more explicit symbolic reasoning.

I tested that. We built a purely symbolic spatial reasoner using predicate logic. "Left(X, Y) ∧ Above(Y, Z) → Left(X, Z)?" It was brittle. A coffee cup rotated 45 degrees and the whole chain broke. Real scenes have noise. Objects aren't perfectly aligned. "On" can mean "physically supported" but also "resting on" but also "attached to but hanging."

The hybrid approach works better. Use symbolic reasoning for the constraints — spatial relations impose geometric constraints that you can solve analytically. But use the learned model for interpretation — what does this person mean by "behind" given their current perspective and the scene context?

Reasoning in Space via Grounding in the World shows exactly this: pure symbolic reasoning hits 72% accuracy on spatial QA. Grounded symbolic (symbols connected to vision) hits 91%. The grounding is the 19-point difference.


The Benchmark That Changed How I Think About This

The Benchmark That Changed How I Think About This

Most spatial reasoning benchmarks are synthetic. Block worlds. Toy scenes. They test whether a model can compute "is A left of B" in a noise-free environment.

The Reframing Spatial Reasoning Evaluation in Language Models paper from IJCAI pushed a simulation benchmark with real-world clutter. Tables with 47 objects. Shelves with partial occlusion. Lighting that casts shadows and makes objects hard to segment.

Our system scored 68% on that benchmark in January 2026. After adding the in-context search sampling and world model updates, we hit 84%. Still not 95% — and that gap matters. The remaining failures come from:

  • Objects that look identical (three identical black mugs)
  • Reference frames that shift mid-sentence ("the cup to the left of the book... wait, no, the one on the right")
  • Relations that require functional understanding ("the pen under the notepad" — the pen is physically under but also inside a drawer under the notepad)

The Do LLMs Construct World Models? cognitive science piece argues that human spatial reasoning is scaffolded by egocentric experience. We know what 30 degrees left feels like because we've turned our head that way. LLMs don't. They're trying to solve a problem without the sensorimotor substrate that the problem was designed for.


What We Got Wrong (Full Confession)

I'll save you six months.

First mistake: We tried end-to-end training. Fine-tune a vision-language model on spatial data. It improved from 55% to 62%. Not worth the compute cost. The model learned correlations, not reasoning. When we changed the lighting, accuracy dropped to 51%.

Second mistake: We ignored reference frames. "Left" needs an origin. Is it the user's left? The object's left? The camera's left? We assumed absolute coordinates. Users don't think in absolute coordinates. They say "my left" meaning their egocentric left. That took two weeks to untangle.

Third mistake: We treated spatial relations as static. "The book on the desk" — but what if the book is partially hanging off the edge? Is it still "on"? The correct answer depends on context. A warehouse robot needs to know if it's safe to push. A person cleaning needs to know if it will fall. The same spatial relation has different grounding depending on the downstream action.


Code You Can Actually Use: A Minimal Grounding System

If you're starting today, here's the minimal scaffold that works for single-frame spatial queries:

python
import numpy as np
from dataclasses import dataclass
from typing import List, Tuple, Optional

@dataclass
class BBox3D:
    center: np.ndarray  # (3,)
    dimensions: np.ndarray  # (3,)
    rotation: np.ndarray  # 3x3 rotation matrix

    def get_corners(self):
        """Return 8 corners of the bounding box."""
        offsets = np.array([
            [-1, -1, -1], [1, -1, -1], [1, 1, -1], [-1, 1, -1],
            [-1, -1, 1], [1, -1, 1], [1, 1, 1], [-1, 1, 1]
        ]) * (self.dimensions / 2)
        return self.center + (self.rotation @ offsets.T).T

class SpatialGrounder:
    def __init__(self, reference_frame="user"):
        self.frame = reference_frame
        self.origin = np.array([0, 0, 0])
        self.forward = np.array([0, 0, -1])  # User looking into -z

    def ground(self, relation: str, target_box: BBox3D,
               anchor_box: Optional[BBox3D] = None) -> float:
        """Return a score from 0 (not satisfied) to 1 (perfectly satisfied)."""
        target_center = target_box.center

        if anchor_box is None:
            origin, forward = self.origin, self.forward
        else:
            origin = anchor_box.center
            forward = anchor_box.rotation @ np.array([0, 0, -1])

        vec = target_center - origin
        dist = np.linalg.norm(vec)

        if dist < 0.01:
            return 0.0

        # Project vec onto horizontal plane (ignore gravity)
        vec_h = vec.copy()
        vec_h[1] = 0
        forward_h = forward.copy()
        forward_h[1] = 0

        if np.linalg.norm(vec_h) < 0.01 or np.linalg.norm(forward_h) < 0.01:
            return 0.0

        # Angle between target direction and forward direction
        cos_angle = np.dot(vec_h, forward_h) / (np.linalg.norm(vec_h) * np.linalg.norm(forward_h))
        angle = np.arccos(np.clip(cos_angle, -1, 1))

        # Cross product for left/right disambiguation
        cross = np.cross(forward_h, vec_h)
        is_left = cross[1] > 0  # Y-up convention

        if relation == "front" and abs(angle) < np.pi / 4:
            return 1.0 - angle / (np.pi / 4) * 0.5
        elif relation == "left" and is_left and abs(angle) > np.pi / 4:
            return min(1.0, (abs(angle) - np.pi / 4) / (np.pi / 2))
        elif relation == "right" and not is_left and abs(angle) > np.pi / 4:
            return min(1.0, (abs(angle) - np.pi / 4) / (np.pi / 2))
        elif relation == "behind" and abs(angle) > 3 * np.pi / 4:
            return min(1.0, (abs(angle) - 3 * np.pi / 4) / (np.pi / 4))

        return 0.0

This is production code from our May 2026 release. It's not perfect. It doesn't handle occlusion. It uses a flat ground plane assumption. But it grounds "front/left/right/behind" with 87% accuracy on clean scenes. That's good enough for the first pass.


The Future: From Spatial Grounding to Spatial Reasoning

The AI Reasoning in Deep Learning Era survey convincingly argues that reasoning emerges from multiple interacting systems. Spatial reasoning is the same. No single model architecture will solve it. You need the perception system, the grounding function, the world model, and the reasoning loop.

We're seeing the early signs of this in the Toward Large Reasoning Models survey on reinforced reasoning. The best spatial reasoners aren't the biggest. They're the ones that can simulate. They build a mental model of the space, run geometric queries against it, and update based on new observations.

What I think happens next:

  • By Q4 2026: Production spatial grounding systems will hit 90%+ accuracy on constrained scenes (warehouses, kitchens, retail shelves). The Improving Zero-Shot Phrase Grounding via Reasoning on Visual Relations approach from AAAI gets us partway there.
  • By Q2 2027: Multi-modal models that fuse vision and language in latent space will approach human-level spatial understanding in narrow domains. But not in open-world.
  • By 2028: The distinction between "perception" and "reasoning" will blur. The world model is the reasoning.

FAQ

Q: Can a pure LLM (no vision) ever understand spatial relations?
A: No. Not in any meaningful sense. It can generate correct strings. But the Grounding Spatial Relations in Text-Only Language Models paper shows it maps spatial language to other language, not to space. That's translation, not understanding.

Q: What's the minimum viable setup for grounding spatial relations?
A: A 3D scene graph with bounding boxes, a coordinate system with defined reference frames, and geometric predicates for each relation you need. Start with 4 relations (left, right, front, behind). Add complexity as you validate.

Q: How do you handle "between" relations?
A: "Between A and B" means the projected point onto the line AB falls within the segment AB, and the perpendicular distance is below a threshold. We also check that the object is visible from both A and B — occlusion breaks "between."

Q: Does gaze tracking improve spatial grounding?
A: Yes, dramatically. In our tests, adding gaze direction as a prior improved accuracy by 18%. When someone says "that cup" while looking at it, the gaze signal resolves ambiguity instantly. But most production systems don't have gaze input, so we use in-context search sampling as a proxy.

Q: What fails that you can't fix?
A: Multiple identical objects in the same spatial region. If three identical black cubes are all within 15 degrees of "left," no amount of grounding helps without additional context. You need color, size, texture, or history. "The one I used last" requires temporal memory.

Q: Is reinforcement learning useful here?
A: Yes, but for policy, not comprehension. Use RL to learn how to act based on grounded spatial understanding. Don't use RL to learn the grounding itself — that's better done with geometric supervision. The Toward Large Reasoning Models survey has good examples.

Q: What's the biggest open problem?
A: Dynamic scenes where objects move during the reasoning process. A person says "the cup to the left of the kettle" while moving the kettle. What's the correct grounding? The answer depends on whether you track the kettle's trajectory or snapshot its position at speech onset. We don't have a unified theory for this.


Last Thing

Last Thing

I started this article by saying text-only models can't see space. That's still true. But the fix isn't to make them "more intelligent" or "more reasoning." The fix is to give them a world model that actually models the world — coordinates, geometry, occupancy, and the messy physical reality that spatial language imperfectly describes.

The grounding spatial relations world model approach works because it doesn't ask the language model to be a spatial reasoner. It asks it to be a translator — from natural language to geometric queries. The geometry does the heavy lifting. The statistics handle the ambiguity. Together, they handle what neither can alone.

At SIVARO, we've shipped this into three production systems in 2026. The warehouses are running. The AR interfaces are responding. The robots are picking the right cup on the left.

And that client who wasted $47,000 on API calls? They're running our on-premise model now. Cost? $3,200 per month. Accuracy? 89%. Turns out the ground truth of spatial relations isn't in the weights. It's in the world.


Nishaant Dixit — Founder of SIVARO. Building data infrastructure and production AI systems since 2018. Built systems processing 200K events/sec.

Free · No Commitment · 48-Hour Delivery

Get a free infrastructure audit

2-hour remote session. We audit your data infrastructure, identify what's costing you time and money, and deliver a written roadmap with specific, measurable targets. No pitch.

Book Your Free Audit
N
Nishaant Dixit
Founder & Lead Engineer at SIVARO

Building data-intensive systems since 2018. 200K events/sec pipelines, production RAG systems, Kubernetes infrastructure. LinkedIn →

Start a Project
Need help with your infrastructure?

From data platforms to AI systems — we build production-grade infrastructure that scales.

Explore Our Services