Every frame the ball takes one real gradient-descent step toward lower ground:
x_new = x - α · ∇f(x)
∇f = ( ∂f/∂x , ∂f/∂z )
The left bowl is convex: f(x,z) = 0.12x² + 0.18z² + 0.05xz. Its Hessian is positive-definite everywhere, so the surface curves upward in every direction from any point — there is exactly one basin, and no matter where the ball starts, gradient descent slides it to the same global minimum. That's the practical payoff of convexity: local information (the slope right under your feet) is enough to guarantee you reach the best point in the whole domain, no search or luck required.
Turning on constraints intersects the bowl with a diamond-shaped feasible region |x| + |z| ≤ R. After each unconstrained step the point is projected back onto the nearest feasible half-space (repeated a few times for the four edges). The ball still converges — just to the best point inside the allowed region, which may sit right on its boundary instead of at the unconstrained minimum.
The right surface swaps in a non-convex function: f(x,z) = 0.02(x²+z²) + 1.5·sin(0.9x)·sin(0.9z), which folds the same quadratic bowl into a rippled egg-carton with many local dips. The exact same update rule is applied — same α, same formula — but depending on the start point it can easily roll into a shallow local minimum and stop there, even though a much deeper valley exists elsewhere on the surface. Gradient descent has no way to tell a local minimum from the global one; convexity is what closes that gap.
- Learning rate α — too small and convergence crawls; too large and the ball overshoots the bowl, visibly oscillating before settling (or diverging on steep regions).
- Click to place — try starting the non-convex ball near a shallow ripple versus near the center; only one of those reaches the true global minimum.
- Convergence — declared once the gradient magnitude at the ball's position drops below a small threshold and stays there.