Quick answer: Store the time of the most recent jump press. On each physics tick, if the player is grounded and the press is within 100ms, consume it. Combine with coyote time for full forgiveness.
Players press jump 1 frame before they land. The press is lost; the character doesn’t jump on contact. Frustrating. Pro platformers buffer presses for a short window.
The Pattern
extends CharacterBody2D
const JUMP_BUFFER = 0.12 # 120 ms
const COYOTE_TIME = 0.10 # 100 ms
var jump_buffer_timer: float = 0.0
var coyote_timer: float = 0.0
func _physics_process(delta):
if Input.is_action_just_pressed("jump"):
jump_buffer_timer = JUMP_BUFFER
if is_on_floor():
coyote_timer = COYOTE_TIME
else:
coyote_timer = max(0, coyote_timer - delta)
jump_buffer_timer = max(0, jump_buffer_timer - delta)
if jump_buffer_timer > 0 and coyote_timer > 0:
velocity.y = JUMP_VELOCITY
jump_buffer_timer = 0
coyote_timer = 0
move_and_slide()
The press is buffered. Each tick: if the player is (or was recently) grounded and a recent press is queued, jump. Both timers consume on jump.
Why It Feels So Much Better
Player presses jump 100ms before landing: with buffer, the input takes effect on the landing frame instead of being lost. Player walks off a ledge and presses jump 50ms later: with coyote time, the jump still executes.
Both windows operate on perception thresholds. Players almost never consciously notice the buffering — they just experience “the game does what I meant.”
Tuning
- Slow puzzle platformers: 150–200ms windows. Players are deliberate; generous buffer.
- Standard action: 100–120ms.
- Speedrun / precision: 60–80ms. Too much buffer makes timing tricks unreliable.
Verifying
Test the pattern: hold jump while character is falling; should jump on landing. Step off a ledge then press jump; should still jump within the coyote window. Both feel responsive without exposing the underlying mechanic.
“Forgiving controls aren’t cheating. They’re respecting the human nervous system’s timing variance.”
Add input buffer day one to any platformer. Cost: 20 lines. Win: significantly tighter feel.