Quick answer: Raise max_slides from the default 4 to 6. Set wall_min_slide_angle around 0.3 radians. Disable slide_on_ceiling for most platformers. Apply horizontal velocity only, not a pre-rotated diagonal, and let move_and_slide resolve the slide direction.

Here is how to fix Godot CharacterBody3D wall slide jitter. Your player pushes into a wall and instead of sliding smoothly along it, the character vibrates or stutters. Sometimes it bounces between two nearby walls in a corner. Sometimes the camera follower shows microshake while the body looks stable. Godot’s built-in move_and_slide is well-tuned for simple cases but has three knobs that interact in non-obvious ways when walls are close or angled.

The Symptom

Pressing into a wall with diagonal input makes the character shiver along the contact. Moving into a concave corner produces a visible bounce. Sloped walls alternate between “treated as floor” and “treated as wall” from frame to frame, snapping the player’s position.

Variant: is_on_wall() toggles true/false every other frame during a slide, breaking animation state machines keyed to wall contact.

What Causes This

max_slides too low. Each call to move_and_slide runs a fixed number of collision resolution iterations (max_slides, default 4). In a corner where the resolved direction bounces off multiple surfaces, 4 is not enough to converge. The solver leaves residual motion, which the next frame tries to resolve — producing oscillation.

wall_min_slide_angle misclassification. Surfaces between vertical and the min-slide angle are treated as walls. Below it, they are treated as floors or ceilings. A sloped surface near the threshold flips classification based on minor normal fluctuation.

slide_on_ceiling interactions. With slide_on_ceiling = true, a character grazing a ceiling slides horizontally. With it off, ceiling contact stops vertical motion. Near overhangs the classification can flip between wall and ceiling.

Velocity recomputed after collision. A common bug: after move_and_slide, user code zeroes or overwrites velocity based on input. Next frame pushes the same velocity back into the wall, producing a step pattern.

Physics ticks vs render ticks. Camera following in _process interpolates between physics states. Without interpolation on the body, the camera shows physics-frame jitter amplified.

The Fix

Step 1: Tune slide parameters.

extends CharacterBody3D

func _ready():
    max_slides = 6
    wall_min_slide_angle = deg_to_rad(15)
    slide_on_ceiling = false
    floor_max_angle = deg_to_rad(45)

max_slides = 6 gives the solver enough passes to converge in sharp corners without measurable cost. Values above 8 rarely help and add compute.

wall_min_slide_angle around 15 degrees (0.26 rad) means anything within 15 degrees of vertical acts as a wall. Below that, it becomes a floor. Pick a value that matches your level geometry — steep ramps should be “floors,” near-vertical cliffs should be “walls.”

Step 2: Preserve velocity after move_and_slide. Do not overwrite velocity from input every frame. Accumulate input, then call move_and_slide, and let it return the actual resolved velocity:

func _physics_process(delta):
    var input_dir = Input.get_vector("left", "right", "fwd", "back")
    var target = Vector3(input_dir.x, 0, input_dir.y) * SPEED

    velocity.x = move_toward(velocity.x, target.x, ACCEL * delta)
    velocity.z = move_toward(velocity.z, target.z, ACCEL * delta)
    velocity.y -= GRAVITY * delta

    move_and_slide()

After move_and_slide, velocity reflects post-collision motion. Let it ride into the next frame rather than recomputing from raw input.

Step 3: Disable slide_on_ceiling for platformers. Jumping into an overhang with slide-on-ceiling enabled makes the character skim along the ceiling, then drop. With it disabled, the character’s upward velocity clears on contact, which feels natural for Mario-style games.

Step 4: Raise solver_iterations for precision. For physics-heavy scenes, tune the Project Settings > Physics > 3D > Solver Iterations value. Default 16 is fine for most games; increase to 24 if you still see residual jitter in tight spaces.

Debug with get_slide_collision

To inspect what the solver actually touched:

for i in get_slide_collision_count():
    var c = get_slide_collision(i)
    print("hit ", c.get_collider(), " normal ", c.get_normal())

If the same body appears multiple times per frame with flipping normals, the solver is bouncing. Raise max_slides or simplify the collision geometry.

Corner Case: Two Walls at Acute Angle

In a 45-degree corner, the naive solve picks one wall, slides, hits the other, slides back, repeat. The fix is projected input: detect wall normals and flatten input into the wedge direction rather than letting the solver fight:

if is_on_wall():
    var n = get_wall_normal()
    velocity = velocity.slide(n)
move_and_slide()

This pre-projects velocity parallel to the dominant wall, reducing solver work and eliminating the bounce pattern.

“Four slides is not enough for a corner. Six is usually plenty.”

Related Issues

For related physics tuning, see Area2D Not Detecting StaticBody2D. For camera jitter that compounds with physics jitter, Viewport Mouse Position Offset shares root-cause territory.

max_slides 6, slide_on_ceiling off, preserve velocity. Smooth slides, no shake.