Home Games Shader Sandbox

Game Dev Mechanics: Procedural Locomotion — How It Works

Procedural Locomotion Move mouse / touch to guide the spider. Scroll to zoom.

What Is Procedural Locomotion?

Every time a spider skitters across a wall in a horror game or a giant mech stomps across uneven terrain, there is a good chance that its legs are not playing a pre-made animation. Instead, the game is computing where each foot should land, when it should lift, and how it should arc through the air, all at runtime. This technique is called procedural locomotion, and it produces movement that adapts organically to any surface, speed, or situation without requiring artists to author hundreds of animation clips.

Traditional keyframed animation works well for flat ground and predictable scenarios, but it breaks down quickly when characters need to walk on slopes, climb over rubble, or navigate moving platforms. Procedural locomotion solves this by treating each leg as an independent agent that follows simple rules: track a home position, step when you drift too far from it, and never step at the same time as your neighbor.

The result is emergent, lifelike movement from a handful of rules. No motion capture studio required.

The Core Algorithm

Procedural locomotion can be broken down into four interlocking systems:

  • Home positions: Each foot has an ideal resting spot relative to the body. As the body moves, so do these targets.
  • Step triggering: When a foot drifts too far from its home position, it initiates a step.
  • Step animation: The foot interpolates from its current position to the new target along a smooth arc.
  • Gait coordination: Neighboring legs are prevented from stepping simultaneously, which keeps the creature balanced and produces natural gait patterns.

These four systems run every frame, constantly evaluating whether any leg needs to move and coordinating the timing so the creature never loses balance. The approach requires zero awareness of terrain shape; the legs simply react to wherever the body goes.

Step 1: Home Positions

Each leg has a home position: the spot on the ground where its foot "wants" to be. This is defined as a fixed offset from the body, projected down onto the ground plane. As the body moves and rotates, the home positions move with it.

For a spider with eight legs, you might define the home offsets in body-local space:

// Each leg has an outward direction and reach distance
const legConfigs = [
  { angle:  40, reach: 2.2 },  // right-front
  { angle: -40, reach: 2.2 },  // left-front
  { angle:  75, reach: 2.2 },  // right-mid-front
  { angle: -75, reach: 2.2 },  // left-mid-front
  // ... four more for the back legs
];

function getHomePosition(legIndex, bodyPos, bodyYaw) {
  const config = legConfigs[legIndex];
  const rad = config.angle * Math.PI / 180 + bodyYaw;
  return {
    x: bodyPos.x + Math.sin(rad) * config.reach,
    y: 0,  // ground level
    z: bodyPos.z + Math.cos(rad) * config.reach
  };
}

The home position is not where the foot is; it is where the foot should be. The gap between these two values is what drives the stepping logic.

Step 2: Triggering a Step

Every frame, each grounded foot measures its distance from its home position. When that distance exceeds a threshold, the foot begins a step. Mathematically:

$$d = \| \vec{p}_{\text{foot}} - \vec{p}_{\text{home}} \|$$

A step is triggered when $d > d_{\text{threshold}}$. A good default threshold is roughly 40–50% of the leg's total reach.

You can improve responsiveness by incorporating the body's velocity into the trigger. When the body is moving fast, legs should step sooner and aim further ahead:

$$d_{\text{effective}} = d + \| \vec{v}_{\text{body}} \| \cdot k$$

where $k$ is a small tuning constant (typically 0.05–0.15). This makes the creature "predict" where it needs to place its feet, eliminating the sluggish feel of a purely reactive system.

function shouldStep(leg, homePos, bodyVelocity) {
  const dist = distance(leg.footPos, homePos);
  const velocityBonus = magnitude(bodyVelocity) * 0.1;
  return dist > STEP_THRESHOLD + velocityBonus;
}

Step 3: Animating the Step

Once a step is triggered, the foot needs to travel from its current position to the new home position. A straight-line interpolation would drag the foot along the ground, so we add a vertical arc using a sine curve:

$$\vec{p}(t) = (1 - s(t)) \cdot \vec{p}_{\text{start}} + s(t) \cdot \vec{p}_{\text{end}}$$

$$y(t) = h \cdot \sin(\pi \cdot t)$$

where $t \in [0, 1]$ is the step progress, $h$ is the peak step height, and $s(t)$ is a smoothstep function for easing:

$$s(t) = 3t^2 - 2t^3$$

The smoothstep ensures the foot accelerates gently off the ground and decelerates before landing, rather than popping into motion abruptly.

function updateStep(leg, dt) {
  leg.stepProgress += dt / STEP_DURATION;
  if (leg.stepProgress >= 1.0) {
    leg.stepProgress = 1.0;
    leg.stepping = false;
    leg.footPos = leg.stepEnd.clone();
    return;
  }
  const t = leg.stepProgress;
  const s = t * t * (3 - 2 * t); // smoothstep
  leg.footPos.x = lerp(leg.stepStart.x, leg.stepEnd.x, s);
  leg.footPos.z = lerp(leg.stepStart.z, leg.stepEnd.z, s);
  leg.footPos.y = Math.sin(t * Math.PI) * STEP_HEIGHT;
}

Step duration is typically very short: 0.1 to 0.25 seconds for a spider. Larger creatures like mechs should use longer durations (0.3–0.6 seconds) to convey weight.

Step 4: Two-Joint Inverse Kinematics

Once we know where the hip and foot are, we need to figure out where the knee (or equivalent middle joint) should go. This is a classic two-joint IK problem, solved analytically with the law of cosines.

Given upper leg length $L_1$, lower leg length $L_2$, and the distance $d$ from hip to foot:

$$\cos\theta = \frac{L_1^2 + d^2 - L_2^2}{2 \cdot L_1 \cdot d}$$

This gives us the angle $\theta$ between the hip-to-foot direction and the upper leg segment. The knee position is then:

$$\vec{p}_{\text{knee}} = \vec{p}_{\text{hip}} + \hat{d} \cdot L_1 \cos\theta + \hat{b} \cdot L_1 \sin\theta$$

where $\hat{d}$ is the unit vector from hip to foot, and $\hat{b}$ is the bend direction, a unit vector perpendicular to $\hat{d}$ that points in the direction we want the knee to bend (typically upward for spider legs).

To compute $\hat{b}$, we project the world "up" vector onto the plane perpendicular to $\hat{d}$ using Gram-Schmidt orthogonalization:

$$\hat{b} = \text{normalize}\left(\vec{u} - (\vec{u} \cdot \hat{d})\hat{d}\right)$$

where $\vec{u} = (0, 1, 0)$.

function solveIK(hip, foot, upperLen, lowerLen) {
  const toFoot = subtract(foot, hip);
  let dist = magnitude(toFoot);
  dist = Math.min(dist, upperLen + lowerLen - 0.01);
  const dir = normalize(toFoot);

  const up = { x: 0, y: 1, z: 0 };
  const dotVal = dot(dir, up);
  const bendDir = normalize({
    x: up.x - dotVal * dir.x,
    y: up.y - dotVal * dir.y,
    z: up.z - dotVal * dir.z
  });

  const cosAngle = (upperLen * upperLen + dist * dist
    - lowerLen * lowerLen) / (2 * upperLen * dist);
  const angle = Math.acos(clamp(cosAngle, -1, 1));

  return {
    x: hip.x + dir.x * Math.cos(angle) * upperLen
             + bendDir.x * Math.sin(angle) * upperLen,
    y: hip.y + dir.y * Math.cos(angle) * upperLen
             + bendDir.y * Math.sin(angle) * upperLen,
    z: hip.z + dir.z * Math.cos(angle) * upperLen
             + bendDir.z * Math.sin(angle) * upperLen
  };
}

Step 5: Gait Coordination

If every leg stepped the instant it needed to, the creature would lift all its feet at once and collapse. Gait coordination prevents this by enforcing a simple rule: a leg may only step if none of its neighbors are currently stepping.

"Neighbors" means the legs immediately adjacent: the same-side leg in front or behind, and the opposite-side leg at the same body position. For an eight-legged spider, this naturally produces an alternating tetrapod gait: four legs move while the other four stay planted, just like real arachnids.

const neighbors = [
  [1, 2],       // leg 0 is blocked by legs 1, 2
  [0, 3],       // leg 1 is blocked by legs 0, 3
  [0, 3, 4],    // leg 2 ...
  [1, 2, 5],
  [2, 5, 6],
  [3, 4, 7],
  [4, 7],
  [5, 6]
];

function canStep(legIndex) {
  return neighbors[legIndex].every(n => !legs[n].stepping);
}

When multiple legs want to step on the same frame, you prioritize by urgency: the leg furthest from its home position steps first. This prevents starvation and ensures the most out-of-place leg always gets serviced.

candidates.sort((a, b) => b.distFromHome - a.distFromHome);
for (const candidate of candidates) {
  if (canStep(candidate.index)) {
    triggerStep(candidate.index);
  }
}

Body Dynamics

A procedurally walking creature looks far more alive when the body responds to the leg positions. Three cheap effects go a long way:

  • Height matching: Set the body's Y position to the average of all foot Y positions plus a fixed offset. This makes the body dip when going downhill and rise on uphill terrain.
  • Velocity tilt: Pitch the body slightly forward when accelerating and backward when decelerating. A multiplier of 0.02–0.05 radians per unit of velocity works well.
  • Step bob: Add a subtle vertical oscillation keyed to the stepping cadence, simulating the natural rise-and-fall of a walking cycle.

$$y_{\text{body}} = \bar{y}_{\text{feet}} + h_{\text{offset}} + A \sin(2\pi f t)$$

where $A$ is a small bob amplitude (0.02–0.05 units) and $f$ is the step frequency.

Velocity-Predictive Stepping

Instead of targeting the current home position, the step target should aim at where the home position will be by the time the foot lands. This is achieved by adding a fraction of the body's velocity to the target:

$$\vec{p}_{\text{target}} = \vec{p}_{\text{home}} + \vec{v}_{\text{body}} \cdot t_{\text{step}}$$

where $t_{\text{step}}$ is the step duration. This lookahead eliminates the lagging artifact common in naive implementations, where feet trail visibly behind the body.

Real-World Examples

Rain World uses procedural locomotion for its slugcat protagonist and most of its creatures. The lizards, vultures, and centipedes all compute foot placement in real time, allowing them to navigate the game's complex pipe-and-pole environment without any pre-authored walk cycles.

Spore was one of the earliest mainstream games to rely entirely on procedural locomotion. Because players could create creatures with any number of legs in any arrangement, Maxis could not author animations in advance, so the game generates gaits at runtime based on body topology.

Star Wars Jedi: Fallen Order uses procedural foot IK on top of keyframed animations. The base walk cycle is hand-animated, but each foot is adjusted procedurally to match uneven terrain, preventing the "floating feet" problem common on stairs and slopes.

Rainforest (tech demo by Ubisoft) demonstrated fully procedural spider locomotion with hundreds of spiders traversing arbitrary mesh surfaces, each computing its own stepping in real time.

Extending the System

The basic system described here handles flat ground well, but real games often need more:

  • Terrain raycasting: Cast a ray downward at each home position to find the actual ground height. This lets the creature walk over bumps, steps, and slopes without modification to the stepping logic.
  • Surface normals: Align the foot orientation to the surface normal at the contact point. This is essential for wall-climbing creatures.
  • Blending with keyframes: Use procedural locomotion for foot placement but blend in keyframed body motion (hip sway, breathing, head bob) for extra personality.
  • Multiple gaits: Vary step duration, step height, and the neighbor constraint based on speed. A slow walk might use a wave gait (one leg at a time), while a sprint uses a bounding gait (all legs on one side together).

Once you understand the approach, animation in games looks different. Instead of fighting with blend trees and transition graphs, you define a few rules and let the motion emerge. The demo above shows these principles in action: move your mouse to guide the spider and watch eight legs coordinate in real time.

Comments

Like this article? Consider supporting us

Your support helps us keep creating free game dev content, tutorials, and tools.

Free

$0 /month

Newsletter and public posts

  • Newsletter access
  • Public posts & updates
  • Community access

Studio Backer

$25 /month

Direct impact on development with your name in the credits

  • Everything in Supporter
  • Your name in game credits
  • Priority feature requests
  • Direct developer access
  • Monthly asset downloads