Gaitkit Sign in Open the lab
Guide

How do procedural IK walk cycles work?

A procedural walk cycle computes each foot's target from how far the body has travelled: a foot in stance stays fixed on the ground while the body moves over it, then swings forward and lands again, with phase offsets between legs setting the gait. Two-bone IK then bends each hip and knee to reach its target. Driving the cycle by distance rather than time is what keeps planted feet from sliding at any speed or frame rate.

Updated 2026-10-22By Gaitkit

Four numbers describe a gait

Every Gaitkit walker publishes its gait as data, so the whole cycle can be rebuilt in any engine. These are the fox's numbers:

catalog.json (shortened)
// the fox, from catalog.json (gait) or get_asset
{
  "stride": 0.15, "duty": 0.7, "lift": 0.035, "clip_speed": 0.375,
  "segments": [
    { "name": "leg_0", "side": "L", "phase": 0.75, "knee": "forward", "upper": 0.11,  "lower": 0.12, "home": [0.048, 0,  0.155] },
    { "name": "leg_1", "side": "L", "phase": 0,    "knee": "back",    "upper": 0.125, "lower": 0.14, "home": [0.048, 0, -0.175] },
    { "name": "leg_2", "side": "R", "phase": 0.25, "knee": "forward", "upper": 0.11,  "lower": 0.12, "home": [-0.048, 0, 0.155] },
    { "name": "leg_3", "side": "R", "phase": 0.5,  "knee": "back",    "upper": 0.125, "lower": 0.14, "home": [-0.048, 0, -0.175] }
  ]
}
  • Stride: how far a planted foot sweeps back relative to the body while it is on the ground, which equals how far the body travels during one stance. The fox's is 15 cm.
  • Duty factor: the share of each cycle a foot spends on the ground. 0.7 means 70% stance and 30% swing. One full cycle covers stride / duty metres: 0.214 m for the fox.
  • Lift: how high a swinging foot rises, at the middle of the swing.
  • Phase: each leg's offset within the cycle, from 0 to 1. Phases decide which feet move together, which is what makes a walk, a trot or a tripod gait.

Each leg also has a home: the point under the hip where the foot sits at mid-stance, in the body's frame.

Gait groups and patterns

Legs with the same phase form a group and move together. Different animals are mostly different phase patterns and duty factors:

  • Biped: two groups half a cycle apart, duty about 0.6, so both feet are down for a moment at each step. The mechanic and the other characters walk like this.
  • Four-legged walk: a lateral sequence, hind-left 0, fore-left 0.75, hind-right 0.5, fore-right 0.25, duty about 0.7. Three feet are down most of the time, as in the fox above.
  • Trot: diagonal pairs move together, duty about 0.55.
  • Six legs: the alternating tripod. Front and back on one side plus the middle on the other share a phase, the other three are half a cycle later; the horn beetle uses this with duty 0.55.
  • Eight legs: a wave gait, each leg offset a little from its neighbour so a ripple runs down each side. The spider bot spreads its phases this way, with duty 0.65.

A duty factor above 0.5 means the groups overlap on the ground, which reads as walking. Below 0.5 there are moments with no feet down, which reads as running or bounding.

Foot targets from distance

The heart of the cycle is a function from distance to foot position. During stance the foot moves backward in the body's frame at exactly the rate the body moves forward, so in the world it stands still. During swing it eases forward by one stride and lifts on a sine arc. This is the logic of walker() in /runtime/walk.mjs, shortened:

JavaScript
const smooth = t => t * t * (3 - 2 * t);

// where one foot should be, in the body's frame, after the body has travelled distance metres
function footTarget(gait, leg, distance) {
  const cycle = gait.stride / gait.duty;                     // metres per full gait cycle
  const t = ((distance / cycle + leg.phase) % 1 + 1) % 1;   // this foot's place in its cycle, 0..1
  const [hx, , hz] = leg.home;
  if (t < gait.duty) {                                       // stance: slide back exactly as fast as the body moves on
    const s = t / gait.duty;
    return [hx, 0, hz + gait.stride * (0.5 - s)];
  }
  const s = smooth((t - gait.duty) / (1 - gait.duty));       // swing: ease forward and lift
  return [hx, Math.sin(Math.PI * s) * gait.lift, hz + gait.stride * (s - 0.5)];
}

In stance, s runs from 0 to 1 while the body covers duty × cycle = stride metres, and the foot's z drops by exactly one stride. The two cancel, which is why the foot does not move in the world. The real walker() also samples the ground height under each foot, raises the body to the average foot height plus a small bob, and can roll a biped's body over its stance leg.

Two-bone IK

Given a hip position and a foot target, a leg with a thigh of length L1 and a shin of length L2 has one solution per bending plane, from the law of cosines:

JavaScript
// two-bone IK in the leg's plane: thigh L1, shin L2, hip-to-foot distance D
function twoBone(L1, L2, D) {
  const clamp = (v, a, b) => Math.min(b, Math.max(a, v));
  D = clamp(D, Math.abs(L1 - L2) + 1e-4, L1 + L2 - 1e-4);                      // out of reach: straighten
  const knee = Math.acos(clamp((L1 * L1 + L2 * L2 - D * D) / (2 * L1 * L2), -1, 1));  // interior angle at the knee
  const hip = Math.acos(clamp((L1 * L1 + D * D - L2 * L2) / (2 * L1 * D), -1, 1));    // thigh vs the hip-to-foot line
  return { hip, knee };
}
// the plane: 'up' knees yaw toward the foot and rise; 'forward' / 'back' use a pole of (0, 0, +1) / (0, 0, -1) in body space

Clamping the distance keeps the solver stable when a target is out of reach, for example when a foot steps into a hole: the leg straightens toward it instead of producing NaN. poseAt returns how far the farthest foot fell short, so you can see when terrain is too rough for a creature's legs.

The bending plane is the other half of the answer. Insects, spiders and crabs bend their knees up: the leg first yaws toward its foot, then the knee rises. A mammal's front leg bends forward, while hocks, bird legs and reverse-jointed mechs bend back; those use a pole vector of (0, 0, +1) or (0, 0, -1) in body space. Each leg's knee field says which.

Why distance, not time

A time-driven cycle plays the legs at a fixed rate and hopes the body moves at the matching speed. When it does not (the creature slows down, the frame rate drops, a physics step stalls), the feet skate. A distance-driven cycle has no rate to mismatch: the pose is a pure function of distance travelled, so a stopped creature stands still mid-step, a fast one takes the same steps sooner, and a scrub bar or an offline render produces the same pose for the same distance.

JavaScript
import { createAsset } from 'https://gaitkit.com/cdn/fox.mjs';
import { walker } from 'https://gaitkit.com/runtime/walk.mjs';

const fox = createAsset();
const gait = walker(fox);          // reads fox.userData.gait
// each frame, after moving the model by the same distance:
const short = gait.poseAt(dist);   // metres the farthest foot is out of reach (0 when every foot lands)

Gaitkit's validator tests this for every walker. It walks the model straight ahead for two full cycles, samples 120 poses per cycle, and fails the build if any foot in stance moves more than 1 mm in the world between samples. The measured value is published per walker as walk_test.foot_slide_mm in /catalog.json.

The baked Walk clip in each GLB is the same cycle sampled on flat ground. Played at speed / gait.clip_speed it is equivalent at a steady speed in a straight line; distance-driven code is what stays correct when speed changes or the ground is uneven.

Limits and porting

  • Turns. Targets are computed in the body's frame, so on a tight turn a planted foot swivels slightly with the body. Wide curves look right; a creature turning on the spot needs extra logic.
  • Speed. Real animals shorten their duty factor as they speed up. Here the duty factor is fixed per creature; walker(model, { speedScale }) lengthens or shortens the stride instead.
  • Porting. Everything above is in the catalogue: stride, duty, lift, and per leg the phase, home, upper and lower lengths and knee direction. The Godot and Unity guides cover playing the baked clip when you do not need terrain-following feet.

Questions

What is duty factor in a walk cycle?

The share of each gait cycle a foot spends on the ground. Walks use more than 0.5, so feet overlap on the ground; runs use less, so there are moments with no foot down.

Why do feet slide in procedural animation?

Usually because the leg cycle is driven by time while the body moves at a different speed. Driving the cycle by distance travelled keeps a planted foot fixed in the world at any speed.

What is two-bone IK?

A closed-form solution that bends a two-segment limb, such as thigh and shin, so its end reaches a target. The law of cosines gives the knee and hip angles, and a pole vector picks the bending plane.

Can I use Gaitkit's gait data in another engine?

Yes. Each walker publishes stride, duty, lift and per-leg phase, home position, segment lengths and knee direction in catalog.json and through get_asset.

Keep looking

Browse by type

Low-poly charactersLow-poly creaturesLow-poly vehiclesLow-poly buildingsLow-poly robots and mechsLow-poly propsLow-poly trees, rocks and terrainLow-poly furnitureLow-poly weaponsLow-poly sci-fi assetsLow-poly fantasy and medievalModular level kits

Motion and price

Free low-poly 3D modelsAnimated low-poly 3D models3D models that walk

By engine

3D models for three.js3D models for React Three FiberLow-poly 3D models for GodotLow-poly 3D models for Unity3D assets for AI agents

Kits

Adventurer GearAir, Sea & SpaceAlien WorldAncient WorldAnimalsBiomesBirds & Sea LifeBug WorldCastleCharactersCity StreetCoastal TownCyberpunkDungeonEveryday PropsFantasy CreaturesFantasy HeroesFarmFoodForest & WildsGarden BugsHalloweenHeavy VehiclesHistoricalHome & GardenHome FurnitureInteriorsKitchen & BathMachinesMech HangarMedieval VillageMilitary BaseModern ArsenalModular Level KitMonster HordeMonstersOffice & TechOutdoorsPiratesPlatformerPlayground & SportsPublic InteriorsRoad VehiclesRobotsSci-Fi CrewSeasonalShop & MarketSolarpunkSpace ColonySports & SkateTabletopTerrainTower DefenseTown BuildingsTownsfolkUnderwaterWastelandWild WestWildlifeWorld Architecture

Guides

How to add a walking 3D creature to a three.js sceneHow to add an animated low-poly model to React Three FiberHow to import an animated low-poly GLB into Godot 4How to use an animated GLB model in UnityHow to give an AI coding agent a 3D asset library over MCPHow to use low-poly 3D assets in a web game