Dynamic data visualization is a skill I frequently utilize, particularly when tracking real-time trends or simulating movements.
In my experience, static plots often fail to tell the whole story, particularly when you need to see how data points evolve. I have spent many hours refining the process of refreshing plots efficiently without crashing the backend or causing flickering.
In this tutorial, I will show you exactly how to update a Matplotlib scatter plot within a loop using the most reliable methods I’ve discovered.
Update Scatter Plots Dynamically
Static charts are great for reports, but real-world data is rarely ever “finished.”
I often work with datasets that stream in real-time, such as stock price fluctuations on the NYSE or tracking GPS coordinates across a city.
If you simply call plt.scatter() inside a loop, Matplotlib will create a new set of axes every time, which quickly consumes your system’s memory.
Instead, you need to update the existing plot object. This makes your visualization smooth and professional.
Method 1: Use the set_offsets() Function (The Most Efficient Way)
When I need the best performance, I always reach for the set_offsets() method. Instead of clearing the entire screen, this method only changes the coordinates of the existing points.
Let’s look at a practical example. Imagine we are tracking the movement of delivery drones across a section of New York City.
import matplotlib.pyplot as plt
import numpy as np
# Set up the simulation environment
plt.ion() # Turn on interactive mode
fig, ax = plt.subplots(figsize=(10, 6))
# USA-specific example: Simulating Drone Deliveries in NYC
# X and Y coordinates representing blocks in a grid
x_coords = np.random.uniform(0, 100, 15)
y_coords = np.random.uniform(0, 100, 15)
# Initialize the scatter plot
scat = ax.scatter(x_coords, y_coords, c='blue', edgecolors='black', s=100)
ax.set_xlim(0, 100)
ax.set_ylim(0, 100)
ax.set_title("Real-Time Tracking: NYC Drone Delivery Fleet")
ax.set_xlabel("East-West Street Grid")
ax.set_ylabel("North-South Street Grid")
# The update loop
for i in range(50):
# Simulate movement by adding small random noise
x_coords += np.random.normal(0, 2, 15)
y_coords += np.random.normal(0, 2, 15)
# Update the data in the scatter object
data = np.stack([x_coords, y_coords]).T
scat.set_offsets(data)
# Pause to allow the GUI to refresh
plt.pause(0.1)
plt.ioff() # Turn off interactive mode
plt.show()I executed the above example code and added the screenshot below.

Using set_offsets is significantly faster because you aren’t re-drawing the axes, labels, or titles.
I’ve found that this is the best approach when you have more than 50 points and need a high frame rate.
Method 2: Use matplotlib.animation.FuncAnimation
In my professional projects, I prefer FuncAnimation over a simple for loop.
It handles the timing and frame management much better, especially if you plan on saving the animation as a video file later.
Let’s use an example of simulating housing price shifts across different US regions.
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
# Data representing 10 major US Cities
cities = ["NYC", "LA", "Chicago", "Houston", "Phoenix", "Philly", "San Antonio", "San Diego", "Dallas", "San Jose"]
x = np.arange(len(cities))
y = np.random.uniform(300, 800, len(cities)) # Median house price in thousands
fig, ax = plt.subplots(figsize=(12, 6))
scat = ax.scatter(x, y, s=200, c='green', alpha=0.6)
ax.set_xticks(x)
ax.set_xticklabels(cities, rotation=45)
ax.set_ylim(200, 900)
ax.set_title("Live Market Pulse: US Median Home Values ($k)")
def update(frame):
# Generate new price data for each "month"
new_y = y + np.random.uniform(-10, 10, len(cities))
# Update the scatter plot offsets
data = np.stack([x, new_y]).T
scat.set_offsets(data)
# Dynamically change color based on price trend
colors = ['red' if val < 500 else 'green' for val in new_y]
scat.set_facecolors(colors)
return scat,
# Create the animation object
ani = animation.FuncAnimation(fig, update, frames=100, interval=200, blit=True)
plt.show()I executed the above example code and added the screenshot below.

I like this method because of the blit=True parameter.
Blitting only redraws the parts of the image that have changed, which prevents that annoying “strobe” effect you sometimes see in loops.
Method 3: The “Clear and Redraw” Approach (Simple but Slow)
Sometimes, you don’t just want to move points; you want to add new points or change the plot entirely. In these cases, I use ax.cla(), which stands for “clear axes.”
I usually use this when I’m plotting cumulative data, such as a voter turnout simulation during an election night.
import matplotlib.pyplot as plt
import random
plt.ion()
fig, ax = plt.subplots(figsize=(10, 6))
voter_x = []
voter_y = []
# Simulating incoming votes at a polling station
for i in range(30):
# New voter arrives
voter_x.append(random.uniform(0, 10))
voter_y.append(random.uniform(0, 10))
ax.cla() # Clear the previous frame
# Re-plot everything
ax.scatter(voter_x, voter_y, color='red', marker='x')
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
ax.set_title(f"Live Election Simulation: {len(voter_x)} Ballots Processed")
plt.pause(0.2)
plt.ioff()
plt.show()I executed the above example code and added the screenshot below.

While this is the easiest to write, I’ve noticed it makes the screen flicker.
Use this only if your data structure changes significantly in every iteration, like adding new clusters.
Common Mistakes to Avoid
In the beginning, I made several mistakes that led to frozen windows or extremely slow code.
One common error is forgetting to include plt.pause(). Without it, Matplotlib doesn’t have time to actually render the frame before the next loop starts.
Another mistake is calling plt.show() inside the loop. This will stop your code entirely until you manually close the window.
Always ensure you are updating the data of the scatter object rather than creating a new scatter object every time.
Adjust Plot Aesthetics Dynamically
I find that visualizations are more impactful when the colors or sizes change based on the data values.
You can update the sizes of your scatter points using scat.set_sizes() or the colors using scat.set_array().
This is particularly useful if you are visualizing something like the intensity of weather patterns across the Midwest.
# Example snippet for updating sizes
new_sizes = np.random.uniform(10, 500, 15)
scat.set_sizes(new_sizes)Adding these small touches makes your data “pop” and helps your audience understand the magnitude of the changes.
Troubleshoot “Non-Responsive” Windows
If your plot window stops responding during a loop, it’s likely because the backend is overwhelmed.
I recommend using a different backend like Qt5Agg or TkAgg if you are on a local machine.
You can set this at the very top of your script using import matplotlib; matplotlib.use(‘TkAgg’).
Also, keep your plt.pause() value reasonable; 0.01 to 0.1 seconds is usually the sweet spot for smooth visuals.
In this article, I have shared the different ways I handle updating scatter plots in a loop.
I have found that starting with a simple loop is great for debugging, but moving to FuncAnimation is better for production.
You may also read:
- Set Python Matplotlib xlim Log Scale
- Update Python Matplotlib Animation Xlim Dynamically
- Adjust the X-Axis Limits in a Matplotlib Heatmap
- Set Matplotlib xlim for Datetime Objects in Python

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.