I Rebuilt the Default Godot Jump to Feel Amazing
Godot 4 · 4:56 · Published July 13, 2026
Godot's default jump technically works, but it ignores what the player meant. This walkthrough rebuilds it in GDScript with variable jump height, coyote time, jump buffering, air control, a fast fall, and corner correction, then adds game-feel juice last.
Chapters
Where the jump code lives
- Player : CharacterBody2Dscriptplayer.gd: variable jump height, coyote time, jump buffering, air control, fast fall, and corner correction all live here.
- AnimatedSprite2D : AnimatedSprite2DAnticipation, stretch, and landing squash — feedback, not physics.
- CollisionShape2D : CollisionShape2DKept unchanged; corner correction nudges the body, it does not resize this.
- Camera2D : Camera2DThe tiny bump after a hard landing.
Transcript
Edited from the video for readability.
0:00 Why your jump feels bad
This is the jump Godot gives you in its default character script, and technically, it works. Tap the button, then hold it, and both jumps reach the exact same height. Walk off a ledge and press jump slightly late: nothing. Press jump just before landing: ignored. Try to correct toward a narrow platform: you miss. Barely touch the corner underneath another platform, and the whole jump gets cancelled.
None of this is technically broken. The controller is doing exactly what the code tells it to do. But it is also ignoring what the player was clearly trying to do. So I put Godot's jump through five tests, and rebuilt it until the game understood what the player meant, not just what they pressed.
0:24 Shaping the arc (gravity + height)
The default jump is simple. Press jump, set the vertical velocity, and add gravity until you land. The first problem is that it uses the same gravity for the entire arc. That works, but the character hangs near the top longer than I want. So I use lighter gravity while rising and stronger gravity while falling.
var gravity := jump_gravity if velocity.y < 0.0 else fall_gravityvelocity.y += gravity * delta0:48 Tap vs hold: variable jump height
The jump now reaches roughly the same height, it just returns to the ground faster. But tapping and holding were still identical. Mario solved this decades ago: tap for a short hop, hold for the full jump. So when the button is released early, I remove some of the remaining upward speed.
Now the player controls the height instead of committing to the exact same jump every time. That is the first test, height, passed. But the jump still only worked if I pressed the button at exactly the right moment.
if Input.is_action_just_released("jump") and velocity.y < 0.0: velocity.y *= 0.451:12 Coyote time & jump buffering
I walked off a ledge and pressed jump right after. That felt like a jump, but the code rejected it because I had already left the floor. Then I pressed jump just before landing, and it was ignored again. Celeste is brutally difficult, but it quietly helps the player like this constantly.
So I added two tiny timers. Coyote time keeps the floor permission alive for one tenth of a second after you leave a ledge, only about six frames at 60 FPS. Jump buffering does the opposite: press jump slightly before landing, and the game remembers the input for twelve hundredths of a second.
Both of these jumps are technically wrong. The player was either already in the air or had not landed yet. But the goal is not to judge the input perfectly, it is to understand what the player meant. That is late jump and early jump passed. Now the game understood when I wanted to jump. It still did not understand where I wanted to land.
if is_on_floor(): coyote_timer = 0.10else: coyote_timer = max(coyote_timer - delta, 0.0)if Input.is_action_just_pressed("jump"): jump_buffer = 0.12else: jump_buffer = max(jump_buffer - delta, 0.0)if coyote_timer > 0.0 and jump_buffer > 0.0: velocity.y = JUMP_VELOCITY coyote_timer = 0.0 jump_buffer = 0.01:55 Controlling where you land
With no air control, your landing is decided the moment you leave the floor. But instant air control is not much better: the character reverses direction immediately in midair, which feels less like jumping and more like steering a shopping cart in space. Different games make different choices. Super Meat Boy gives you strong correction because its levels demand precision; heavier platformers preserve more momentum through the arc. For this controller, I use slower acceleration in the air.
Now I can correct a bad landing without deleting all my momentum. Then I added two smaller controls. First, a few extra frames of control near the apex. I do not want the whole jump to float; I only want a tiny amount of extra control at the exact moment the player is aiming their landing. And if I am already falling, pressing down activates a fast fall.
Variable height controls the way up, air acceleration controls where you land, and fast fall gives you control on the way down. That is the landing test passed.
var acceleration := ground_accelerationif not is_on_floor(): acceleration = air_accelerationvelocity.x = move_toward(velocity.x, direction * max_speed, acceleration * delta)if Input.is_action_pressed("down") and velocity.y > 0.0: velocity.y += fast_fall_gravity * delta2:59 The corner problem
The character barely touched the corner underneath a platform, and its upward motion immediately stopped. Most of the character clearly fits, but one corner touched the ceiling, so the entire jump was cancelled. Technically correct, visually ridiculous.
Celeste uses tiny collision allowances for exactly this reason: it would rather preserve a jump that looks successful than punish a collision the player can barely see. So when only one side of the player is blocked, I check for open space on the other side. If there is room, I move the player a few pixels toward it. This is not enough to push the player through a wall; it only corrects a tiny mistake that already looked successful. That is the corner test passed, five out of five.
3:34 Game feel & juice
The controller had passed every movement test. Now I could finally make it look good. Games like Ori make every movement readable before you even think about the code underneath it. Before takeoff, the character compresses; while rising, it stretches; and when it lands, it squashes. Then I added dust, a takeoff sound, a landing sound, and a tiny camera bump after hard falls.
The character is jumping, not destroying a building. Feedback can explain good movement, but it cannot create it. You cannot particle your way out of a bad jump.
4:07 Before & after
So this was Godot's default controller: tap and hold are identical, late jump fails, early jump fails, the narrow landing is missed, the corner collision cancels the jump, and the landing has no response.
And this is the same controller now: a short hop, a full-height jump, a late coyote jump, a buffered landing jump, a midair correction onto the narrow platform, a fast fall onto the moving platform, corner correction, and a final landing with squash, dust, and sound. The same idea carries into 3D: short and full-height jumps, air correction, a coyote jump, a buffered landing, and a hard-landing response.
Underneath all of this, it is still just velocity, gravity, and input. But now the player controls the arc, the game accepts reasonable inputs, and tiny mistakes no longer ruin jumps that already looked successful.
4:29 Wrap-up & demo
A good jump gives the player control on the way up, control on the way down, and a little forgiveness when their intent is obvious. Feedback makes that movement readable, but it cannot replace it. You cannot particle your way out of a bad jump.
I put the complete 2D and 3D controllers on Coding Quests so you can compare every value against Godot's default script. Because there is no perfect jump, only the jump that does what your player meant.
Build this yourself
The video is the tour. The interactive quest is where you write every value yourself and compare it against Godot's default.
- Quest: A Jump That Feels Amazing
- Read: Game Juice: Making the Brackeys Platformer Actually Feel Good
- Reference project on GitHub


