3D Surface Plot Animations in Python using Matplotlib
In this tutorial, you’ll learn various methods to animate 3D surface plots, from simple rotations to complex morphing effects.
You’ll use libraries like Matplotlib and NumPy to generate dynamic three-dimensional data.
Rotate Surface Plot
To create a rotating 3D surface plot, you’ll use Matplotlib animation functionality along with a basic surface plot.
import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation def f(x, y): return np.sin(np.sqrt(x**2 + y**2)) x = np.linspace(-6, 6, 30) y = np.linspace(-6, 6, 30) X, Y = np.meshgrid(x, y) Z = f(X, Y) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') surf = ax.plot_surface(X, Y, Z, cmap='viridis') def animate(frame): ax.view_init(elev=30, azim=frame) return surf, anim = FuncAnimation(fig, animate, frames=np.linspace(0, 360, 100), interval=50, blit=False) plt.show()
This code creates a 3D surface plot of a sine function and animates it by rotating the view around the z-axis.
The animate
function updates the viewing angle for each frame.
Evolve Surfaces Over Time
You can create an evolving surface by updating the Z values in each animation frame.
import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation x = np.linspace(-5, 5, 50) y = np.linspace(-5, 5, 50) X, Y = np.meshgrid(x, y) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') def f(x, y, t): return np.sin(np.sqrt(x**2 + y**2) - t) surf = ax.plot_surface(X, Y, f(X, Y, 0), cmap='coolwarm') def animate(frame): ax.clear() surf = ax.plot_surface(X, Y, f(X, Y, frame/10), cmap='coolwarm') ax.set_zlim(-1, 1) return surf, anim = FuncAnimation(fig, animate, frames=100, interval=50, blit=False) plt.show()
This animation shows a surface evolving over time.
The f
function now includes a time parameter t
, which is updated in each frame to create a wave-like motion across the surface.
Color-changing Surfaces
To create a color-changing surface, you’ll update the colormap in each frame of the animation.
import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation from matplotlib.colors import LinearSegmentedColormap x = np.linspace(-5, 5, 50) y = np.linspace(-5, 5, 50) X, Y = np.meshgrid(x, y) Z = np.sin(np.sqrt(X**2 + Y**2)) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') def create_colormap(t): return LinearSegmentedColormap.from_list("custom", [(0, "blue"), (t, "green"), (1, "red")]) surf = ax.plot_surface(X, Y, Z, cmap=create_colormap(0)) def animate(frame): ax.clear() surf = ax.plot_surface(X, Y, Z, cmap=create_colormap(frame/100)) ax.set_zlim(-1, 1) return surf, anim = FuncAnimation(fig, animate, frames=100, interval=50, blit=False) plt.show()
This code creates a surface plot with a dynamically changing colormap.
The create_colormap
function generates a new colormap for each frame and transfers from blue to green to red as the animation progresses.
Morph between different surfaces
To morph between different surfaces, you’ll interpolate between two or more surface functions.
import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation def f1(x, y): return np.sin(np.sqrt(x**2 + y**2)) def f2(x, y): return (x**2 - y**2) / 10 x = np.linspace(-5, 5, 50) y = np.linspace(-5, 5, 50) X, Y = np.meshgrid(x, y) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') def morph(x, y, t): return (1-t) * f1(x, y) + t * f2(x, y) surf = ax.plot_surface(X, Y, morph(X, Y, 0), cmap='viridis') def animate(frame): ax.clear() t = frame / 100 surf = ax.plot_surface(X, Y, morph(X, Y, t), cmap='viridis') ax.set_zlim(-1, 1) return surf, anim = FuncAnimation(fig, animate, frames=100, interval=50, blit=False) plt.show()
This animation smoothly transitions between two different surface functions.
The morph
function linearly interpolates between f1
and f2
based on the time parameter t
.
Animated Contour Plots
You can create animated contour plots by updating the contour levels in each frame.
import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation x = np.linspace(-5, 5, 100) y = np.linspace(-5, 5, 100) X, Y = np.meshgrid(x, y) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') def f(x, y, t): return np.sin(np.sqrt(x**2 + y**2) - t) def animate(frame): ax.clear() Z = f(X, Y, frame/10) contour = ax.contourf(X, Y, Z, zdir='z', offset=-2, cmap='viridis') surf = ax.plot_surface(X, Y, Z, alpha=0.7, cmap='viridis') ax.set_zlim(-2, 2) return surf, contour anim = FuncAnimation(fig, animate, frames=100, interval=50, blit=False) plt.show()
This animation combines a surface plot with an animated contour plot beneath it.
The contour plot updates along with the surface, creating a dynamic visualization of the changing function.
Grow or Shrink Surfaces
To create a growing or shrinking surface, you’ll modify the domain of the function over time.
import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation fig = plt.figure() ax = fig.add_subplot(111, projection='3d') def f(x, y): return np.sin(np.sqrt(x**2 + y**2)) def animate(frame): ax.clear() t = frame / 100 x = np.linspace(-5*t, 5*t, 50) y = np.linspace(-5*t, 5*t, 50) X, Y = np.meshgrid(x, y) Z = f(X, Y) surf = ax.plot_surface(X, Y, Z, cmap='coolwarm') ax.set_xlim(-5, 5) ax.set_ylim(-5, 5) ax.set_zlim(-1, 1) return surf, anim = FuncAnimation(fig, animate, frames=100, interval=50, blit=False) plt.show()
This animation shows a surface growing from the center outwards.
Wave Propagation on Surfaces
To simulate wave propagation on a surface, you’ll use a time-dependent function that creates wave-like patterns.
import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation x = np.linspace(-5, 5, 100) y = np.linspace(-5, 5, 100) X, Y = np.meshgrid(x, y) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') def wave(x, y, t): return np.sin(np.sqrt(x**2 + y**2) - t) * np.exp(-0.1 * (x**2 + y**2)) surf = ax.plot_surface(X, Y, wave(X, Y, 0), cmap='coolwarm') def animate(frame): ax.clear() Z = wave(X, Y, frame/10) surf = ax.plot_surface(X, Y, Z, cmap='coolwarm') ax.set_zlim(-1, 1) return surf, anim = FuncAnimation(fig, animate, frames=200, interval=50, blit=False) plt.show()
This animation simulates a wave propagating outward from the center of the surface.
The wave
function combines a sine wave with an exponential decay to create a realistic wave-like effect.
Slice Through 3D Surfaces
To create an animation that slices through a 3D surface, you’ll update a plane that intersects the surface in each frame.
import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation x = np.linspace(-5, 5, 50) y = np.linspace(-5, 5, 50) X, Y = np.meshgrid(x, y) Z = np.sin(np.sqrt(X**2 + Y**2)) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') surf = ax.plot_surface(X, Y, Z, alpha=0.7, cmap='viridis') def animate(frame): ax.clear() surf = ax.plot_surface(X, Y, Z, alpha=0.7, cmap='viridis') # Create slicing plane xx, zz = np.meshgrid(x, np.linspace(-1, 1, 10)) yy = np.full_like(xx, frame/10 - 5) ax.plot_surface(xx, yy, zz, alpha=0.5, color='r') ax.set_zlim(-1, 1) return surf, anim = FuncAnimation(fig, animate, frames=100, interval=50, blit=False) plt.show()
This animation shows a red plane moving through the 3D surface and slices it.
The plane’s position is updated in each frame to create the slicing effect.
Animated Vector Fields on Surfaces
To create animated streamlines or vector fields on a surface, you’ll combine a surface plot with quiver plots that change over time.
import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation x = np.linspace(-5, 5, 20) y = np.linspace(-5, 5, 20) X, Y = np.meshgrid(x, y) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') def f(x, y): return np.sin(np.sqrt(x**2 + y**2)) Z = f(X, Y) def vector_field(x, y, t): u = -y * np.cos(t) v = x * np.cos(t) w = np.sin(t) * np.ones_like(u) return u, v, w surf = ax.plot_surface(X, Y, Z, alpha=0.7, cmap='viridis') def animate(frame): ax.clear() surf = ax.plot_surface(X, Y, Z, alpha=0.7, cmap='viridis') t = frame / 20 u, v, w = vector_field(X, Y, t) ax.quiver(X, Y, Z, u, v, w, length=0.5, normalize=True, color='r') ax.set_zlim(-1, 1) return surf, anim = FuncAnimation(fig, animate, frames=100, interval=50, blit=False) plt.show()
This animation combines a static surface plot with a dynamic vector field represented by red arrows.
The vector field rotates over time, creating the illusion of flow across the surface.
The vector_field
function generates the u, v, and w components of the vectors, which are updated in each frame of the animation.
Mokhtar is the founder of LikeGeeks.com. He is a seasoned technologist and accomplished author, with expertise in Linux system administration and Python development. Since 2010, Mokhtar has built an impressive career, transitioning from system administration to Python development in 2015. His work spans large corporations to freelance clients around the globe. Alongside his technical work, Mokhtar has authored some insightful books in his field. Known for his innovative solutions, meticulous attention to detail, and high-quality work, Mokhtar continually seeks new challenges within the dynamic field of technology.