Creating animated 3D plots in Python

Matplotlib has become the standard plotting library in Python. This is thanks to its simple API and NumPy/SciPy integration, making it easy to add interactive plots to any code.

In this post, I will walk through how to make animated 3D plots in Matplotlib, and how to export them as high quality GIFs.

RGB Color space example

Colors in computer graphics are usually represented as a combination of levels of red, green, and blue. This is an artifact of display technology history, as well as the nature of additive color. Each combination of red, green, and blue is plotted as a point on a discrete cube, forming the RGB color space (shown above in 6-bit color depth).

To create this animation, first we make our necessary imports.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from mpl_toolkits.mplot3d import Axes3D

Then we create our figure and axis. Notice the projection='3d' argument on the add_subplot method. Since we’re plotting different colors, I set the background to 50% gray.

fig = plt.figure()
fig.subplots_adjust(left=0, bottom=0, right=1, top=1)
ax = fig.add_subplot(111, projection='3d')
ax.set_facecolor((0.5, 0.5, 0.5))

Now let’s plot a color-coded meshgrid onto a scatter plot.

gradient = np.linspace(0, 1, 2)
X,Y,Z = np.meshgrid(gradient, gradient, gradient)
colors=np.stack((X.flatten(),Y.flatten(),Z.flatten()),axis=1)
ax.scatter(X,Y,Z,alpha=1.0,s=50,c=colors,marker='o',linewidth=0)
plt.axis('off')
fig.set_size_inches(5, 5)

We can now animate it by using FuncAnimation, changing the azimuth to rotate.

def update(i, fig, ax):
    ax.view_init(elev=20., azim=i)
    return fig, ax

anim = FuncAnimation(fig, update, frames=np.arange(0, 360, 2), repeat=True, fargs=(fig, ax))
anim.save('rgb_cube.gif', dpi=80, writer='imagemagick', fps=24)

Here is the result:

That looks cool. We can add precision with some simple adjustments, highlighted below:

bits = 8
fig = plt.figure()
fig.subplots_adjust(left=0, bottom=0, right=1, top=1)
ax = fig.add_subplot(111, projection='3d')
ax.set_facecolor((0.5, 0.5, 0.5))
gradient = np.linspace(0, 1, 2**bits)
X,Y,Z = np.meshgrid(gradient, gradient, gradient)
colors=np.stack((X.flatten(),Y.flatten(),Z.flatten()),axis=1)
ax.scatter(X,Y,Z,alpha=1.0,s=100./2**bits,c=colors,marker='o',linewidth=0)

Here is the result:

Awesome! I like the the low fidelity appeal of the lower precision cubes. Also, notice that the diagonal between the white and black corners are all shades of gray.

There are some problems. I could not plot more than 6 bits per channel in a reasonable amount of time. The plot does not respect the viewpoint-dependent stack order of the points. I’m not sure how to solve this issue without re-plotting every frame, but it’s pretty cool either way!


About the author



Hi, I'm Nathan. I'm an electrical engineer in the Los Angeles area. Keep an eye out for more content being posted soon.


Leave a Reply

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