Skip to content
Published February 18, 2022

Sometimes the outcomes of our models are very complicated and are worth showing as a short video or animation. The best format for a short animation is a gif file. If the outcome is longer and contains more than ~100 frames it is better to use video format because they allow some additional compression.

In the following example I demonstrate how to create a simple animation using Python. One of the best discussions is available here. For the input function I will use the sinus with constantly increasing frequency.

First we import all packages which we use:

from pygifsicle import optimize
import os
import imageio
from math import *
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
plt.rc('font', family='serif') ## setting for figures which I usually use in my publications
mpl.rcParams.update({'font.size': 12})
mpl.rcParams.update({'legend.labelspacing':0.25, 'legend.fontsize': 12})
mpl.rcParams.update({'errorbar.capsize': 4})

I change standard parameters for font in Matplotlib, so the final product can be inserted in an article. I use package pygifsicle to make the final gif more compact. In this example the file is compressed from 19 MB to 4.9 MB with a single command. Next I write a function which generates data for individual frame. Instead of data generation it is possible to read individual files in this function.

## Define a function which produces numerical input for our individual frame.
## It could be simply a fuction which reads individual files 

def fun (i, x): ## argument of the function is the frame number

        return np.sin ((i/30.0+1)*x) ## we increase frequency in each frame 

At the next stage we plot our data and save them as individual png files.

filenames = []

N = 300 ## Total number of separate frames in animation

x = np.linspace (0, 2*pi, 200)

for i in range (0, N):

        y = fun (i, x)

        plt.plot (x,y) #, rasterized=True)

        filename = 'frame_'+str(i)+'.png'

        filenames.append (filename)

        plt.xlim([0, 2.0*pi]) ## This adds stability to the axis
        plt.ylim([-1.1, 1.3])

        plt.text (1.5*pi, 1.1, r'$\omega=$'+str(round(i/30.0+1, 1)))

        plt.savefig (filename)
        #plt.show() ## this part could be used for debugging to see how individual frame looks like.
        plt.close()     

plt.close() is used here to clean information from current plot, so the next frame does not have data shown at the previous frame. If our data are three-dimensional we can use plt.imshow() instead of plt.plot().

At the final step we call function get_writer from package imageio. This function combines all our individual figures into a single .gif file. After this step we can remove individual .png files.

gif_filename = 'sinus_gif.gif'

with imageio.get_writer(gif_filename, mode='I') as writer:
        for filename in filenames:  ## here we go through all prepared files
                image = imageio.imread(filename) 
                writer.append_data(image)

                os.remove(filename) ## now we remove individual frames

optimize(gif_filename) ## to optimise the file size