2024-01-23 (c) H.Paananen, AsamaLab www.asamalab.com Here is explained how to greate an animated GIF with Python's Pillow, that does not do looping, i.e. runs only once. Applies at least animation.py <= v.3.8.2 Background: ------------------------------------------------------------------------------------------- An animated GIF can be generated using Pillow, which is included to MatPlotLib. A list of still images created with pyplot can be animated with animation.FuncAnimation class. That seems to accept an argument "repeat", which may suggest that, if set to False will prevent looping. However, it appears that the "repeat" is ignored, and the generated GIF always loops infinitely. What actually happens deep inside the implementation is that at the final stage of animation creation PilloWriter's finish() method gets called. The method looks like that: class PillowWriter(AbstractMovieWriter): ... def finish(self): self._frames[0].save( self.outfile, save_all=True, append_images=self._frames[1:], duration=int(1000 / self.fps), loop=0) The argument "loop=0" will instruct Pillow's GifImagePlugin.py to add an "Application extension" to GIF's header, which automatically forces the GIF to do looping (loop value 0 means "forever"). To prevent looping, the loop argument should not be given at all. A fix: ------------------------------------------------------------------------------------------- A quick solution is to overload the finish() method. One way to do it is just inherit the whole class and overload the method. Here I created a new class "NoLoopPillowWriter" from matplotlib import animation class NoLoopPillowWriter(animation.PillowWriter): # Inherit PillowWriter def finish(self): self._frames[0].save( self.outfile, save_all=True, append_images=self._frames[1:], duration=int(1000 / self.fps)) # Not having 'loop' will run seq only once and use it in the animator like this: anim.save(filename, writer=NoLoopPillowWriter(fps=fps),) A better way would be to pass the number of loops via the kwargs when PillowWriter instance is initialized, and hack the class to pass the kwargs all the way to GifImagePlugin.py which then looks up the "loop" and decides what to do (loop=None would be a neat way to tell that do not do looping). For example: anim.save(filename, writer=PillowWriter(fps=fps, loop=None),) (or alternatively perhaps loop=0 would be "no looping" and loop<0 "forever", or something else...)