---
title: "Why Single-Shot LLM Games Tunnel Through Walls: Swept-AABB, Coyote Time, and Spatial Hashing"
slug: "why-single-shot-llm-games-fail-physics"
date: "2026-10-18"
author: "Taylor Amarel"
category: "3D Game Physics & Simulation"
reading_time: "9 min read"
canonical_url: "https://blog.tayloramarel.com/posts/why-single-shot-llm-games-fail-physics.html"
markdown_url: "https://blog.tayloramarel.com/posts/why-single-shot-llm-games-fail-physics.md"
paper_pdf: "https://papers.tayloramarel.com/With_vs_Without_Baltor_Single_File_3D_Fox_Survival_Game_Animations_and_LongForm_Prompts_2026.pdf"
evaluation_portal: "https://compare.baltor.ai/games"
summary: "A side-by-side dissection of raw prompt-generated games vs. harvested open-source physics engines in Godot, Bevy, and Three.js."
---

# Why Single-Shot LLM Games Tunnel Through Walls: Swept-AABB, Coyote Time, and Spatial Hashing

**By Taylor Amarel** · 2026-10-18 · 3D Game Physics & Simulation · 9 min read  
*Related Paper:* [https://papers.tayloramarel.com/With_vs_Without_Baltor_Single_File_3D_Fox_Survival_Game_Animations_and_LongForm_Prompts_2026.pdf](https://papers.tayloramarel.com/With_vs_Without_Baltor_Single_File_3D_Fox_Survival_Game_Animations_and_LongForm_Prompts_2026.pdf) · *Live Benchmark:* [https://compare.baltor.ai/games](https://compare.baltor.ai/games)

---

Ask any frontier language model to generate a playable 3D action game, platformer, or racing simulation in a single prompt, and the resulting code almost always fails in the first ten seconds of gameplay. The character falls through the floor when walking up a hill, tunnels straight through thin walls during a frame-rate dip, or feels unresponsive when jumping off a ledge. In our side-by-side comparison studio ([compare.baltor.ai/games](https://compare.baltor.ai/games)) and standalone 3D worlds ([demos.baltor.ai/foxglade](https://demos.baltor.ai/foxglade)), we tested raw single-shot LLM game outputs against games assembled from verified open-source physics and controller components harvested from Godot, Bevy, and Three.js repositories.

The failure of single-shot LLM games is not an aesthetic problem. It is a mathematical failure caused by three shortcuts that appear in nearly every tutorial snippet in a language model's pretraining corpus: **unclamped discrete Euler integration**, **missing swept Time-of-Impact (TOI) collision resolution**, and **frame-dependent jump state machines without coyote time or input buffering**.

### Shortcut 1: Unclamped Discrete Euler Step and Wall Tunneling

When a language model writes a `requestAnimationFrame` loop from scratch, it almost universally writes discrete position integration coupled with a post-step overlap check:

```
// Typical raw LLM movement loop (BROKEN on frame spikes or fast projectiles)
player.position.x += player.velocity.x * deltaTime;
player.position.z += player.velocity.z * deltaTime;
if (boxIntersectsWall(player.box, wall.box)) {
    player.position.x -= player.velocity.x * deltaTime;
}
```

Why does this break immediately in a real browser? Suppose your character or projectile moves at `18.0 units/s` toward a wall or fence that is `0.25 units` thick. At a smooth 60 FPS (`deltaTime = 0.0166s`), the displacement per frame is `0.30 units`. If a garbage-collection pause or shader compilation causes a single frame to take `0.045s`, the displacement jumps to `0.81 units`. On frame `t_0`, the player's bounding box is completely in front of the wall; on frame `t_1`, it is completely behind the wall. The discrete post-step check `boxIntersectsWall` evaluates to `false` on both frames, and the player tunnels out of the arena.

Even when the discrete check catches an overlap, reversing the full displacement (`position -= velocity * dt`) leaves a visible gap in front of the wall and prevents the player from sliding smoothly along diagonal obstacles. If the player presses W+D into a 45-degree canyon wall, raw LLM physics glues the player in place.

### Shortcut 2: Flat Y=0 Ground Clamping Instead of Barycentric Heightfields

In 3D outdoor environments like our woodland survival benchmark ([demos.baltor.ai/foxglade](https://demos.baltor.ai/foxglade)), unassisted models generate visual hills by displacing vertex heights in a plane mesh, but then write character grounding as `if (fox.position.y < 0) fox.position.y = 0;`. As a result, the character walks horizontally through the middle of every hill and floats above every valley. Furthermore, the character mesh stays rigidly horizontal (`rotation.x = 0`) on 30-degree slopes instead of pitching to match the surface normal.

### Shortcut 3: Exact-Frame Ground Booleans vs. Coyote Time & Jump Buffering

In human-playable platformers and parkour games, players press the jump key slightly before touching the ground (within 80–120 ms of landing) or slightly after running off a ledge (within 80–110 ms of leaving the platform). Raw LLM games gate jumping on a brittle instantaneous boolean: `if (keys.Space && isGrounded) velocity.y = JUMP_IMPULSE;`. Because `isGrounded` flips to `false` on the exact millisecond the player's center crosses the ledge edge, 35% to 50% of human jump inputs are silently dropped, making the game feel broken.

### How the Baltor Physics Slot Solves All Three Problems

Instead of letting the LLM improvise collision math inline, our `physics_controller_3d` engine slot injects a verified 4-stage kinematic solver adapted from production Godot `CharacterBody3D.move_and_slide()` and Bevy spatial-hash patterns:

  - **Fixed Sub-Stepping with Delta Clamping:** Frame deltas are clamped to `max_dt = 0.05s` and subdivided into fixed `1 / 120s` physics sub-steps so high-velocity motion never skips thin colliders.
  - **Swept-AABB Continuous Collision Detection (Minkowski Sum Raycast):** For each sub-step, we expand static obstacle AABBs by the moving character's half-extents and intersect the movement ray against the expanded box to compute exact entry time `t_entry in [0, 1]` and contact normal `n`.
  - **Tangent-Plane Velocity Projection (Slide Response):** When a collision occurs at `t_entry`, the character advances to `t_entry - epsilon`, and the remaining velocity vector is projected onto the contact tangent plane (`v_slide = v - dot(v, n) * n`) for up to 3 slide iterations.
  - **Barycentric Terrain Normal Alignment + 110ms Coyote / Buffer Timers:** Terrain height and surface normals are sampled analytically across triangle barycentrics, while a `0.11s` coyote timer and `0.11s` jump-input buffer guarantee crisp responsiveness.

### Empirical Comparison Across Six Playable 3D Game Worlds

We ran automated 600-frame adversarial stress tests (including simulated 25 FPS frame spikes, diagonal wall-sliding, and ledge-edge jump inputs) across all six game worlds in [compare.baltor.ai/games](https://compare.baltor.ai/games), holding the model fixed at `qwen3-coder:30b` / `gemma4:31b`:

| 3D Game World (compare.baltor.ai/games) | Raw LLM Tunneling / Clip Rate | Baltor Swept-AABB Tunneling Rate | Ledge Jump Input Drop (Raw vs. Baltor) | 60 FPS Frame Budget (Spatial Hash vs. O(N^2)) |
| --- | --- | --- | --- | --- |
| 1. Foxglade: The Living Wood | 34.0% hill/bridge clip | 0.0% (Barycentric + Catenary) | 42.0% -> 0.0% | 4.2 ms (14,000 grass blades instanced) |
| 2. Outpost Zero (Survival Arena) | 28.5% projectile wall tunnel | 0.0% (Swept-AABB TOI) | 38.0% -> 0.0% | 3.1 ms (Spatial hash grid cell=4m) |
| 3. Canyon Circuit 3D (Racer) | 41.2% barrier glue / clip | 0.0% (3-Pass Tangent Slide) | N/A (Suspension raycast) | 2.8 ms |
| 4. Skyline Hop (3D Parkour) | 31.0% moving-platform drop | 0.0% (110ms Coyote + Carry) | 48.5% -> 0.0% | 2.4 ms |
| 5. Lantern Keep (Stealth Puzzle) | 22.0% corner snag | 0.0% (Minkowski Corner Slide) | 35.0% -> 0.0% | 2.9 ms |
| 6. Meadow Courier (Flight/Glide) | 29.4% ridge penetration | 0.0% (Continuous Swept Sphere) | 33.0% -> 0.0% | 3.0 ms |

### Concrete Implementation: Swept-AABB Time-of-Impact & Tangent Slide

Below is the core continuous collision and slide resolver from our verified `physics_controller_3d` component slot that powers our browser worlds:

```
export function sweptAABBTimeOfImpact(movingBox, vel, staticBox) {
  // Minkowski sum: expand staticBox by movingBox half-extents
  const hx = (movingBox.maxX - movingBox.minX) * 0.5;
  const hz = (movingBox.maxZ - movingBox.minZ) * 0.5;
  const cx = (movingBox.minX + movingBox.maxX) * 0.5;
  const cz = (movingBox.minZ + movingBox.maxZ) * 0.5;

  const minX = staticBox.minX - hx, maxX = staticBox.maxX + hx;
  const minZ = staticBox.minZ - hz, maxZ = staticBox.maxZ + hz;

  const invVx = Math.abs(vel.x) > 1e-6 ? 1.0 / vel.x : Infinity;
  const invVz = Math.abs(vel.z) > 1e-6 ? 1.0 / vel.z : Infinity;

  const tx1 = (minX - cx) * invVx, tx2 = (maxX - cx) * invVx;
  const tz1 = (minZ - cz) * invVz, tz2 = (maxZ - cz) * invVz;

  const tEntryX = Math.min(tx1, tx2), tExitX = Math.max(tx1, tx2);
  const tEntryZ = Math.min(tz1, tz2), tExitZ = Math.max(tz1, tz2);

  const tEntry = Math.max(tEntryX, tEntryZ);
  const tExit = Math.min(tExitX, tExitZ);

  if (tEntry > tExit || tEntry  1.0) {
    return { hit: false, toi: 1.0, nx: 0, nz: 0 };
  }
  const nx = tEntryX > tEntryZ ? (vel.x > 0 ? -1 : 1) : 0;
  const nz = tEntryZ >= tEntryX ? (vel.z > 0 ? -1 : 1) : 0;
  return { hit: true, toi: Math.max(0.0, tEntry - 1e-3), nx, nz };
}
```

### Try the Side-by-Side Physics Comparison Live

You can toggle between the raw single-shot LLM builds and the Baltor-harnessed builds across all six playable 3D worlds at [compare.baltor.ai/games](https://compare.baltor.ai/games), or launch the full-bleed woodland world directly at [demos.baltor.ai/foxglade](https://demos.baltor.ai/foxglade).
