When Your Plotly Chart Gets Slow: Switching from SVG to WebGL with Scattergl
Plotly's default SVG rendering chokes on large scatter plots. Switching to scattergl (WebGL) is a one-line change — here's when it's worth it, what you give up, and how to verify on your own machine.
14 Mar 2026, 13:47 UTC

The chart that froze the browser
You load a few hundred thousand sensor readings into a Plotly scatter chart, open it in the browser, and try to zoom. The tab hangs. Hover tooltips lag by seconds. Nothing is wrong with your data — the problem is the rendering backend.
By default, Plotly draws 2D traces as SVG: every point becomes a DOM element. SVG is crisp and styleable, but browsers struggle to keep tens of thousands of DOM nodes interactive. The fix is usually one line: switch the trace to scattergl, which draws the same chart with WebGL on the GPU instead of the DOM.
The thesis of this post is simple: pick your rendering backend based on data size and the features you need, not by habit. SVG for small, richly annotated charts; WebGL for large point clouds where smooth pan and zoom matter more than exotic styling.
What actually changes when you switch
With go.Scatter, each marker is an SVG node. Layout, hit-testing for hover, and zoom all run through the browser's DOM pipeline, which is why interactivity falls off a cliff somewhere in the tens-of-thousands-of-points range (the exact cliff depends on your hardware and browser — treat any number here as rough guidance, not a guarantee).
With go.Scattergl, points are rasterized by the GPU. The DOM stays tiny, and pan/zoom stays fluid well into the hundreds of thousands of points. The Python API is nearly identical — same x, y, mode, and most marker options — so for the common case it's a drop-in replacement.
A worked example you can time yourself
Run this locally (needs plotly and numpy installed; any recent Plotly 5.x works). It builds the same 300,000-point chart twice — once in SVG, once in WebGL — and writes both to HTML files you can open side by side:
import numpy as np
import plotly.graph_objects as go
rng = np.random.default_rng(42)
n = 300_000
x = rng.normal(size=n)
y = x * 0.5 + rng.normal(size=n)
# SVG version: expect sluggish zoom/hover
fig_svg = go.Figure(go.Scatter(
x=x, y=y, mode="markers",
marker=dict(size=3, opacity=0.4),
))
fig_svg.write_html("scatter_svg.html")
# WebGL version: same data, one-word change
fig_gl = go.Figure(go.Scattergl(
x=x, y=y, mode="markers",
marker=dict(size=3, opacity=0.4),
))
fig_gl.write_html("scatter_gl.html")Open both files in your browser and try dragging a zoom box and panning. On most machines the SVG file will feel sticky or freeze outright, while the WebGL file stays responsive. If you use Plotly Express, the equivalent switch is even friendlier:
import plotly.express as px
fig = px.scatter(df, x="reading", y="voltage",
render_mode="webgl") # default is "svg"One honest caveat about timing: I'm not going to quote you benchmark numbers, because they're hardware- and browser-dependent. The meaningful test is the one above on your own machine, with your real data volume. Also note that WebGL depends on the viewer's browser and GPU — headless environments or very old browsers may fall back or fail, so if your audience views charts in locked-down environments, verify there first.
The trade-off: feature parity
Scattergl is not a complete superset of Scatter. Historically, some SVG features have been limited or missing in WebGL mode — certain marker symbols, some error-bar configurations, and per-point text rendering are the usual suspects. The exact gaps shift between Plotly releases, so don't trust any fixed list (including a memorized one from me): check the changelog or docs for your installed version with pip show plotly, and test the specific features your chart needs.
That leads to a practical decision rule:
- Under ~10–20k points, or heavy annotation/error bars/custom markers: stay with SVG. It's fully featured and plenty fast at that scale.
- Hundreds of thousands of points, mostly markers/lines, interactivity matters: use
scatterglorrender_mode="webgl". - Millions of points: even WebGL will strain. Downsample or aggregate first — bin into a 2D histogram or heatmap, or pre-aggregate in pandas — and plot the summary instead.
What to do on Monday
Find the slowest chart in your current project, duplicate it with scattergl, and compare pan/zoom in the browser. If the WebGL version renders everything you need, keep it; if you hit a missing feature, either drop back to SVG with a downsampled dataset or pre-aggregate. The point isn't that WebGL is "better" — it's that Plotly gives you two backends with different strengths, and choosing deliberately takes about five minutes.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.