3D Game Physics & Simulation

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

By Taylor Amarel · 2026-10-18 · 9 min read (1238 words)
DISCRETE EULER TUNNELING (RAW LLM) VS. SWEPT-AABB + BARYCENTRIC HEIGHTFIELD (BALTOR HARNESS) WITHOUT HARNESS: NAIVE DISCRETE STEP (x += v * dt) t_0 (before wall) t_1 (tunneled through!) At 30 FPS spike (dt=33ms), v*dt > wall thickness -> 0 overlap. WITH BALTOR: SWEPT-AABB TOI + SLIDE PLANE + COYOTE TIME t_hit = 0.61 (TOI clamped + tangent slide) Continuous raycast finds exact Time-of-Impact; 0% wall tunneling.

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) and standalone 3D worlds (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), 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:

  1. 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.
  2. 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.
  3. 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.
  4. 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, 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 < 0.0 || 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, or launch the full-bleed woodland world directly at demos.baltor.ai/foxglade.


More Technical Essays on blog.tayloramarel.com

Date Article Markdown
2026-10-18 Better Model or Better Harness? Breaking Down the Six-Part Agent Harness Raw .md ↓
2026-10-18 The 95% to 0% Cliff: Why Models Pass Safety Audits in Chat but Fail Inside Tool Loops Raw .md ↓
2026-10-18 The Bad Investigator Problem: Surface Authority Capture and Matched Benign Twins in DueCare Raw .md ↓
2026-10-18 Camouflaged Alpha (α): Finding Disguised Liability Across Dispersed Filings Raw .md ↓
2026-10-18 Jev Evaluations: Stress-Testing Quantized, Sub-2-Bit, and Local Open-Weight Models Under Deterministic Gates Raw .md ↓
2026-10-18 From Wildlife Video to 49-Bone WebGL2 Rigs: How Field Notes 01 Works Raw .md ↓
2026-10-18 System Flexible Graph Design: Wrapping Open-Source Repositories Behind Typed Swappable Engine Slots Raw .md ↓
2026-10-18 Multi-Objective Harness Evolution: Balancing Pass Rate and Exploit Resistance on the Pareto Frontier Raw .md ↓
2026-10-18 Stop Copy-Pasting the Internet into Your Coding Agent: Machine-Readable Contract Feeds vs. Human Documentation Raw .md ↓