Quick answer: CharacterBody2D jitter along walls is typically caused by the velocity vector fighting the wall normal on every physics frame.
Here is how to fix Godot CharacterBody2D jittering sliding walls. You are moving your character along a wall and instead of a smooth slide, the sprite jitters, stutters, or vibrates in place. This is one of the most common physics complaints in Godot 4 platformers, and it almost always comes down to how move_and_slide() handles velocity when the character is pressed against a surface. The good news: it is fixable with a few targeted adjustments.
The Symptom
When your CharacterBody2D walks or runs into a wall at an angle, the character visually shakes or oscillates instead of sliding smoothly. The effect is most noticeable when the player holds a diagonal input against a vertical wall, or when a character lands on a sloped surface and tries to move horizontally. In some cases, the jitter is subtle — a one-pixel vibration. In others, the character visibly bounces back and forth several pixels every frame.
The jitter tends to get worse at higher movement speeds and is often accompanied by inconsistent is_on_floor() or is_on_wall() readings. You might notice the character briefly losing floor contact when walking along the base of a wall, or the wall flag flickering between true and false on consecutive frames.
If you enable Debug → Visible Collision Shapes in the editor, you can often see the collision shape itself oscillating, confirming that the issue is in the physics response rather than a rendering or animation problem.
What Causes This
The root cause is a feedback loop between your input velocity and the collision response from move_and_slide(). Here is what happens frame by frame:
- Your
_physics_process()sets the velocity based on player input, pointing partly into the wall. move_and_slide()detects the wall collision and subtracts the component of velocity that pushes into the wall, leaving only the component parallel to the surface.- On the next frame, your input code overwrites the velocity again with the full input direction, re-introducing the perpendicular component.
- The collision system corrects it again, but this time the character has moved slightly, so the correction pushes it in the opposite micro-direction.
This back-and-forth creates the visible jitter. The problem is amplified when wall_min_slide_angle is set too low (or left at the default), because the engine tries to slide along surfaces that are nearly perpendicular to the character’s movement. It also gets worse if max_slides is high, because the engine performs multiple collision corrections per frame, each one introducing a tiny positional offset.
Another contributing factor is floor detection threshold. If your character is near the junction of a wall and a floor, the engine can alternate between treating the contact as a floor collision and a wall collision, causing the velocity to be adjusted differently on alternating frames.
The Fix
There are three adjustments that together eliminate wall jitter in nearly every case. Apply them incrementally and test after each one.
Step 1: Adjust wall_min_slide_angle. This property controls the minimum angle a surface needs to have before the character will attempt to slide along it. The default value is 0.2618 radians (about 15 degrees). For most 2D platformers with vertical walls, you can increase this slightly:
func _ready():
wall_min_slide_angle = deg_to_rad(20.0)
max_slides = 4
Setting it to 20 degrees means the engine will not attempt to slide along surfaces that are nearly vertical, which eliminates most of the oscillation.
Step 2: Zero out the perpendicular velocity component after collisions. After calling move_and_slide(), check whether the character is on a wall and remove the velocity component that pushes into it:
func _physics_process(delta):
# Apply input velocity
var input_dir = Input.get_axis("move_left", "move_right")
velocity.x = input_dir * SPEED
# Apply gravity
if not is_on_floor():
velocity.y += gravity * delta
move_and_slide()
# Eliminate wall jitter by zeroing velocity into the wall
for i in get_slide_collision_count():
var collision = get_slide_collision(i)
var normal = collision.get_normal()
# If this is a wall (normal is mostly horizontal)
if abs(normal.x) > 0.9:
velocity.x = 0.0
This prevents leftover velocity from carrying into the next frame and re-triggering the collision loop.
Step 3: Stabilize floor detection near wall junctions. If the jitter specifically happens where walls meet the floor, increase the floor_snap_length to keep the character firmly grounded:
func _ready():
floor_snap_length = 8.0
floor_max_angle = deg_to_rad(50.0)
A floor_snap_length of 8 pixels gives the engine enough margin to maintain floor contact even when minor collision corrections push the character slightly upward. The floor_max_angle of 50 degrees ensures that angled surfaces near the wall base are still treated as floors rather than walls.
Why This Works
The jitter is fundamentally a feedback loop: input pushes into wall, collision pushes back, input pushes in again. Each of the three fixes breaks a different part of this loop.
Increasing wall_min_slide_angle tells the engine to stop trying to slide along near-perpendicular surfaces. When the character hits a vertical wall, the engine simply stops the movement in that direction rather than attempting a tiny slide that creates positional noise.
Zeroing the perpendicular velocity prevents the input code from re-introducing the problematic velocity component on the next frame. Without this, your _physics_process() overwrites the cleaned-up velocity with the raw input direction, restarting the oscillation cycle.
Increasing floor_snap_length stabilizes the character at wall-floor junctions. Without sufficient snap length, the collision correction from the wall can push the character just far enough off the ground that is_on_floor() returns false. When gravity re-applies, the character snaps back down, only to be pushed up again by the next wall correction. The increased snap length absorbs these micro-corrections without losing floor contact.
Together, these three changes produce a character that slides cleanly along walls without any visible oscillation, even at high speeds and at wall-floor intersections.
Related Issues
Wall jitter shares root causes with several other CharacterBody2D problems. If your character slides down slopes when idle, the same floor_snap_length and floor_max_angle tuning applies. Characters that appear to fall through the floor at high speeds may also benefit from the velocity-zeroing technique described above, though RigidBody2D objects require a different approach using continuous collision detection. And if your collision normals seem wrong, that can directly cause the wall jitter described here, since the engine uses those normals to calculate the slide direction.