Finally I wrote a bit of Python code to display Gifs in a Jupyter notebook.
I use these libraries:
IpyWidgets
and IPython.display.HTML
for UI
Matplotlib.Pyplot
to display frames as images
Matplotlib.animation
to animate
os
to read the file
NumPy
to read a frame in an array
PIL
to process image pixels
The code can be easily adapted to work without Jupyter, but notebooks are a great tool for interacting:
import os
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation, rc
from PIL import Image
import ipywidgets as widgets
from IPython.display import HTML
reader = GifReader('Muybridge_cavall_animat.gif')
fig,ax = plt.subplots()
ax.axis('off')
data = reader.seek(0)
h = ax.imshow(data)
plt.close()
def make_frame(n):
data = reader.seek(n)
h.set_data(data)
return [h]
anim = animation.FuncAnimation(fig, make_frame,
frames=reader.get_length()-1,
blit=True)
HTML(anim.to_jshtml())

Original gif image
The Reader class:
class GifReader:
pil_image = None
length = -1
def __init__(self, file_path):
self._open(file_path)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.close()
def _open(self, file_path):
if self.pil_image != None:
raise ValueError(f'File is already open')
if os.path.isfile(file_path) == False:
raise ValueError(f'File {file_path} doesn\'t exist')
with open(file_path, 'rb') as f:
if f.read(6) not in (b'GIF87a', b'GIF89a'):
raise ValueError('Not a valid GIF file')
self.pil_image = Image.open(file_path)
self.length = self.pil_image.n_frames
def close(self):
if self.pil_image == None: return
self.pil_image.close()
self.pil_image = None
def get_length(self):
if self.pil_image == None:
raise ValueError('File is closed')
return self.length
def seek(self, n):
if self.pil_image == None:
raise ValueError('File is closed')
if n >= self.length:
raise ValueError(f'Seeking frame {n} while maximum is {self.length-1}')
self.pil_image.seek(n)
a = self.pil_image.convert ('RGB')
return a