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.

Leave a Reply

Your email address will not be published. Required fields are marked *