Skip to content

Game Feel · Timing · common

Coyote Time

let the player jump a few frames after leaving the ledge

Coyote time keeps a jump available for ~0.1 s after the character walks off an edge, so presses that feel on time are not eaten by the game.

View .md
Interactive visual

The cube runs off the ledge and the player presses jump at the yellow line, a little late. The green strip is the grace window. Runs alternate between coyote time on (green, the jump works) and off (grey, it falls into the red pit). Slide the press later than the window to see it fail even with coyote time.

What it is

Players judge a ledge by eye, and the button press reaches the game a few frames after they decide. A strict platformer only allows a jump while grounded is true, so a press that lands one or two frames after the feet leave the edge does nothing and the character drops. To the player it feels like the game ignored the input.

Coyote time fixes this with a timer. While grounded, keep it full (e.g. 0.1 s). Once airborne, count it down with delta time. A jump is allowed while grounded || coyoteTimer > 0. When the jump fires, set the timer to zero so it cannot be used twice.

It is invisible when tuned well: the jump simply always seems to work at the edge. It pairs naturally with input buffering, which forgives presses that come slightly too early instead of too late.

Key formulas

Jump allowed
Extra ledge distance

At 6 u/s and 0.1 s the character can be 0.6 units past the edge and still jump.

Rules of thumb

  • Start with 0.08–0.15 s (about 5–9 frames at 60 fps). Over ~0.2 s it starts to feel like a mid-air jump.
  • Only start the timer when the character walks off. Jumping should zero it, or the player gets a free second jump.
  • Measure in seconds, not frames, so the window is the same at every frame rate.
  • Many games also ignore gravity or cap the fall speed during the window so the jump still starts from near ledge height.

Implementation

Coyote timer inside a character update:
const COYOTE = 0.1 // seconds
let coyoteTimer = 0

function update(dt: number, jumpPressed: boolean) {
  if (player.grounded) coyoteTimer = COYOTE
  else coyoteTimer = Math.max(0, coyoteTimer - dt)

  if (jumpPressed && (player.grounded || coyoteTimer > 0)) {
    player.velocity.y = JUMP_SPEED
    coyoteTimer = 0 // consume it: no double jump
    player.grounded = false
  }
  player.velocity.y -= GRAVITY * dt
  player.position.addScaledVector(player.velocity, dt)
}

Common mistakes

  • Not clearing the timer on jump, which lets players jump again during the first frames of a jump.
  • Counting frames instead of seconds, so the window shrinks to 40 ms on a 144 Hz monitor.
  • Refilling the timer when the character touches walls or ceilings, which turns it into a wall-jump exploit.
  • Making the window so long that walking off a cliff and then jumping looks like a double jump.

Prompt

Add coyote time to my TypeScript Three.js platformer controller. Keep a coyoteTimer in seconds: refill it to 0.1 s every update the character is grounded, and count it down by dt while airborne. Allow a jump when grounded or coyoteTimer > 0; when the jump fires, set the timer to 0 and set vertical velocity to JUMP_SPEED. Do not refill on wall or ceiling contact. Expose COYOTE as a tunable constant. Write unit tests that step the controller at 30 and 144 fps: a press 0.05 s after walking off a ledge jumps, a press 0.15 s after does not, and pressing twice never yields two jumps.

Further reading

Updated 2026-11-01