Here is the practical way to achieve it with Python:
import numpy as np
import matplotlib.pyplot as plt
# Some scalar function of interest:
def z(x, y):
return np.power(np.sin(x), 3) + np.power(np.cos(y), 3)
# Grid for gradient:
xmin, xmax = -7, 7
x = y = np.linspace(xmin, xmax, 100)
X, Y = np.meshgrid(x, y)
# Compute gradient:
dZ = np.gradient(Z, x, y)
# Gradient magnitude (arrow colors):
M = np.hypot(*dZ)
# Grid for contour:
xh = yh = np.linspace(xmin, xmax, 400)
Xh, Yh = np.meshgrid(xh, yh)
# Plotting gradient & contour:
fig, axe = plt.subplots(figsize=(12, 12))
axe.contour(Xh, Yh, Zh, 30, cmap="jet", linewidths=0.75)
axe.quiver(X, Y, dZ[1], dZ[0], M, cmap="jet", units='xy', pivot='tail', width=0.03, scale=5)
axe.set_aspect("equal") # Don't stretch the scale
axe.grid()
It renders:

There are two visualizations of interest to see the gradient:
As expected gradient is orthogonal to contour curves.