import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
#plt.rcParams["animation.html"] = "jshtml"
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(12, 8))
def update(frame):
date = dates[frame]
day_data = daily_groups[date]
ax.scatter(
day_data["Longitude"],
day_data["Latitude"],
s=day_data["TargetValue"],
alpha=0.6,
color="crimson",
edgecolors="black"
)
ax.set_title(f"Confirmed Cases on {date}")
ax.set_xlim([-300, -65])
ax.set_ylim([24, 50])
ax.set_xlabel("Longitude")
ax.set_ylabel("Latitude")
ax.grid(True)
return ax,
anim = FuncAnimation(fig, update, frames=len(dates), interval=200)
plt.tight_layout()
plt.show()
This plot doesn't show, but I can save it
anim.save("us_cases_animation.mp4", writer="ffmpeg", fps=5)
Video runs, this happens both on VS code and Jupyter notebook. I only get a blank fig with axis. I used to get IPython undefined, I don't get that anymore though.
when I use this:
%matplotlib ipympl
I get this error "Error displaying widget"
-
plt.show(block=True) should workChandan Saroj– Chandan Saroj2025年10月16日 04:58:06 +00:00Commented Oct 16 at 4:58
-
@ChandanSaroj When I use %matplotlib ipympl I get this error "Error displaying widget"Bertrand Wittgenstein's Ghost– Bertrand Wittgenstein's Ghost2025年10月16日 05:43:10 +00:00Commented Oct 16 at 5:43
-
This wasn't close to being a minimal reproducible example as many thing were undefined. Thankfully, Chandan Saroj did the heavy lifting. However, please read How do I ask a good question?, especially the section 'Help others reproduce the problem', and follow it in the future.Wayne– Wayne2025年10月16日 16:22:39 +00:00Commented Oct 16 at 16:22
2 Answers 2
FuncAnimation creates the animation object, but does not automatically render in the notebook. you just did plt.show() that works for scripts, but in Jupyter it only shows a static blank figure with axes.
To display animation it requires Explicit inline display:
from IPython.display import HTML
HTML(anim.to_jshtml())
Tried with sample code
# Required for Jupyter notebook
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from IPython.display import HTML
dates = ["Sun", "Mon", "Tue", "Wed", "Thu"]
num_points = 20
latitudes = np.random.uniform(24, 50, (len(dates), num_points))
longitudes = np.random.uniform(-125, -65, (len(dates), num_points))
sizes = np.random.uniform(50, 300, (len(dates), num_points))
# figure and axis
fig, ax = plt.subplots(figsize=(10, 6))
ax.set_xlim([-130, -60])
ax.set_ylim([20, 55])
ax.set_xlabel("Longitude")
ax.set_ylabel("Latitude")
ax.grid(True)
# Initialize scatter
scat = ax.scatter([], [], s=[], color='crimson', alpha=0.6, edgecolors='black')
# animation func
def update(frame):
global scat
# Required
scat.remove()
scat = ax.scatter(
longitudes[frame],
latitudes[frame],
s=sizes[frame],
color='crimson',
alpha=0.6,
edgecolors='black'
)
ax.set_title(f"Confirmed Cases: {dates[frame]}")
return scat,
#Create animation
anim = FuncAnimation(fig, update, frames=len(dates), interval=500, blit=True)
#Display animation
HTML(anim.to_jshtml())
and if it is blank canvas problem, remove previous points using scat.remove() before plotting new points.
1 Comment
This works fine with typical Jupyter with the ipykernel to use ipympl to display the animation without needing to save a file or make HTML:
%matplotlib ipympl
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
#plt.rcParams["animation.html"] = "jshtml"
dates = ["Sun", "Mon", "Tue", "Wed", "Thu"]
num_points = 20
latitudes = np.random.uniform(24, 50, (len(dates), num_points))
longitudes = np.random.uniform(-125, -65, (len(dates), num_points))
sizes = np.random.uniform(50, 300, (len(dates), num_points))
fig, ax = plt.subplots(figsize=(12, 8))
#day_data = daily_groups[date]
scat = ax.scatter(
longitudes[0],
latitudes[0],
s=sizes[0],
alpha=0.6,
color="crimson",
edgecolors="black"
)
ax.set_title(f"Confirmed Cases on {dates[0]}")
ax.set_xlim([-300, -65])
ax.set_ylim([24, 50])
ax.set_xlabel("Longitude")
ax.set_ylabel("Latitude")
ax.grid(True)
def update(frame):
x = longitudes[frame]
y = latitudes[frame]
s=sizes[frame]
data = np.stack([x, y]).T
scat.set_offsets(data)
ax.set_title(f"Confirmed Cases: {dates[frame]}")
return scat
anim = FuncAnimation(fig, update, frames=len(dates), interval=200)
See it in action without touching your own system...
To see it work without environment concenrs, go here, click on one if the 'launch binder' badges. When the temporary remote session comes up, open a new notebook and paste in the above code.
There are simpler versions of the framework for that under 'Use of Matplotlib's animation.FuncAnimation()' in the fifth and sixth code cells in that notebook of examples that starts up when the sessions starts. It is always best to start with a working example and adapt it to plot what you want.
For JupyterLite with the pyodide kernel, it seems at this time to use FuncAnimation() you need to generate the frames ahead of time/make HTML (,and so this case ends up being closer to what Chandan Saroj says).
Put this in the cell and run it:
%matplotlib ipympl
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
#plt.rcParams["animation.html"] = "jshtml"
dates = ["Sun", "Mon", "Tue", "Wed", "Thu"]
num_points = 20
latitudes = np.random.uniform(24, 50, (len(dates), num_points))
longitudes = np.random.uniform(-125, -65, (len(dates), num_points))
sizes = np.random.uniform(50, 300, (len(dates), num_points))
fig, ax = plt.subplots(figsize=(12, 8))
#day_data = daily_groups[date]
scat = ax.scatter(
longitudes[0],
latitudes[0],
s=sizes[0],
alpha=0.6,
color="crimson",
edgecolors="black"
)
ax.set_title(f"Confirmed Cases on {dates[0]}")
ax.set_xlim([-300, -65])
ax.set_ylim([24, 50])
ax.set_xlabel("Longitude")
ax.set_ylabel("Latitude")
ax.grid(True)
def update(frame):
x = longitudes[frame]
y = latitudes[frame]
s=sizes[frame]
data = np.stack([x, y]).T
scat.set_offsets(data)
ax.set_title(f"Confirmed Cases: {dates[frame]}")
return scat
anim = FuncAnimation(fig, update, frames=len(dates), interval=200)
plt.close(fig) # Prevent static plot display
from IPython.display import HTML
HTML(anim.to_jshtml())
Use the controller with the play button below the plot to run the animation frames.
How you can try this in JupyterLite:
Go to here and click on 'try lite now' badge.
Alternatively, go to Try Jupyter Page and click on either of the top two tiles here for the 'JupyterLab' or 'Jupyter Notebook' tile.
When the interface opens, make a new notebook and then follow the steps above.
Alternative implementations compatible with either typical ipykernel or JupyterLite pyodide kernel
If you don't stipulate needing FuncAnimation(), these are compatible with either kernel-type so don't require as much adapting for use in different locations at this time:
First, based on the examples at the top of here using clear_output() with a pause that don't use player widget and calculate frames in advance of displaying the animation (so it is more of a live display of the current state):
from IPython.display import clear_output
from matplotlib import pyplot as plt
import numpy as np
import time
dates = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri","Sat"]
num_points = 20
latitudes = np.random.uniform(24, 50, (len(dates), num_points))
longitudes = np.random.uniform(-125, -65, (len(dates), num_points))
sizes = np.random.uniform(50, 300, (len(dates), num_points))
def live_plot(frame,date):
clear_output(wait=True)
#day_data = daily_groups[date]
plt.figure(figsize=(12, 8))
plt.scatter(
longitudes[frame],
latitudes[frame],
s=sizes[frame],
alpha=0.6,
color="crimson",
edgecolors="black"
)
plt.title(f"Confirmed Cases on {date}")
plt.xlim([-300, -65])
plt.ylim([24, 50])
plt.xlabel("Longitude")
plt.ylabel("Latitude")
plt.grid(True)
plt.show()
time.sleep(0.75) # extend delay between adding next frame in animation
for indx,date in enumerate(dates):
live_plot(indx,date)
Second, based on the example for ipywidget's interact() near the top of here:
# based on https://stackoverflow.com/q/76212356/8508004
import ipywidgets as widgets
import numpy as np
import matplotlib.pyplot as plt
dates = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri","Sat"]
num_points = 20
latitudes = np.random.uniform(24, 50, (len(dates), num_points))
longitudes = np.random.uniform(-125, -65, (len(dates), num_points))
sizes = np.random.uniform(50, 300, (len(dates), num_points))
def plt_func(t):
plt.figure(figsize=(10, 10))
plt.scatter(
longitudes[t],
latitudes[t],
s=sizes[t],
alpha=0.6,
color="crimson",
edgecolors="black"
)
date = dates[t]
plt.title(f"Confirmed Cases on {date}")
plt.xlim([-300, -65])
plt.ylim([24, 50])
plt.xlabel("Longitude")
plt.ylabel("Latitude")
plt.grid(True)
plt.show()
widgets.interact(plt_func, t = widgets.Play(min=0, max = len(dates)-1, interval = 450), continuous_update = True);
Like the main offered JupyterLite-compatible version there, the interact()-based version involves a controller widget. This particular one uses a 'play' widget in the upper left corner that you have to activate to start things by pressing the 'play' button.
This controller widget is more limited though than the one from anim.to_jshtml(), with only play and stop and a toggle for looping.
3 Comments
'launch binder' takes it from the situation 'it works on my computer'. Or in this case, doesn't work. ipympl & ipywidgets can be difficult. Did you do a hard refresh of your notebook browser page after restarting the kernel and restarting everything, evExplore related questions
See similar questions with these tags.