Plotly Scattergl vs Scatter: WebGL Performance Guide
Need to plot hundreds of thousands of points in Plotly? Learn why Scattergl outperforms Scatter, how to switch with a single line, and what features drop out. Practical example included.
12 Jan 2026, 18:16 UTC

Why the switch matters
When you plot more than a few thousand points with Plotly’s go.Scatter, the browser starts to choke. Each point becomes an individual SVG element in the DOM, so the number of nodes grows linearly with the data size. Rendering, layout, and hover calculations then become proportional to that node count, which quickly degrades to a frozen tab. The go.Scattergl trace solves this by delegating the drawing to WebGL: the points are sent to the GPU as a single buffer, and the browser renders them as a single textured quad. The result is a dramatic drop in DOM size, faster initial paint, and smooth pan/zoom even with hundreds of thousands of points.
How it works under the hood
Plotly’s SVG traces create a <path> or <circle> element for every data point. The browser’s layout engine must keep track of each element, which inflates memory usage and slows down JavaScript callbacks that handle interactivity. In contrast, Scattergl uses a WebGL shader program. The data array is uploaded once to the GPU as a Float32Array buffer. The GPU then draws all points in a single pass, using the same fragment shader for every vertex. Because the GPU is designed for massive parallelism, the operation stays fast regardless of the point count.
One‑line switch
Changing from SVG to WebGL is literally a one‑line change in your Python code. All other trace attributes—axes, titles, hover formatting, colors—transfer unchanged.
import plotly.graph_objects as go
import numpy as np
# generate 500k random points
x = np.random.randn(500_000)
y = np.random.randn(500_000)
# SVG version (slow for large data)
fig_svg = go.Figure(go.Scatter(x=x, y=y, mode='markers'))
fig_svg.update_layout(title='SVG Scatter (500k points)')
fig_svg.show()
# WebGL version (fast)
fig_gl = go.Figure(go.Scattergl(x=x, y=y, mode='markers'))
fig_gl.update_layout(title='Scattergl (500k points)')
fig_gl.show()
Run the script in a Jupyter notebook or a local Python environment. Open the resulting HTML in a modern browser (Chrome, Firefox, Edge). You should see the Scattergl plot appear almost instantly and remain responsive when you pan or zoom, whereas the Scatter plot will lag or freeze.
What you can and can’t do with Scattergl
- Supported features: Basic marker color, size, opacity, and simple line styles work fine. Hover text and tooltips are available, but the tooltip content is limited to the nearest point.
- Limitations:
- Complex marker symbols (e.g.,
'square','diamond') are not rendered; only the default circle is guaranteed. - Line dash styles and certain line colors are partially supported; dashed lines may not appear.
- Text annotations attached to points are not displayed; only the point itself is drawn.
- Error bars are ignored in
Scattergl.
- Complex marker symbols (e.g.,
- Export caveats: When you export a
Scatterglfigure withfig.write_image(Kaleido), the trace is rasterized to PNG or JPEG. PDF or SVG exports will contain a bitmap of the plot, not a vector representation. - Browser support: WebGL must be enabled in the viewer’s browser. Some corporate or educational browsers disable it by default, causing the trace to fall back to SVG or fail to render.
Common mistakes to avoid
- Assuming full visual parity: Don’t expect
Scatterglto look identical toScatterwhen you use advanced markers or error bars. Verify the visual output before relying on it. - Overlooking export needs: If you need a vector graphic for print or high‑resolution PDFs, stick with
Scatteror pre‑aggregate your data. - Ignoring WebGL availability: Test the target environment (e.g., a shared JupyterHub) for WebGL support. You can run
import plotly; plotly.io.show()and check the console for WebGL errors. - Sending raw millions of points: Even though
Scatterglhandles them, serializing a 10‑million‑point array into the notebook can bloat the file and slow load times. Consider downsampling or using a server‑side rendering approach.
When to use a hybrid approach
For interactive exploration, Scattergl is often the best choice. If you need a clean SVG for further editing in Illustrator or for static PDFs, consider one of these strategies:
- Pre‑aggregate the data into bins or use a density heatmap.
- Render a
Scatterglplot for the live view, then export a raster image for documentation. - Use
plotly.express.lineor other trace types that are lightweight for small subsets.
Practical checks and verification steps
- Verify the installed Plotly version:
import plotly; print(plotly.__version__). TheScatterglAPI has evolved; older releases may lack certain attributes. - Confirm WebGL availability by opening the browser console and looking for messages like
WebGL: no contextorWebGL: context lost. - After plotting, inspect the DOM: the SVG trace will contain
<circle>elements equal to your point count;Scatterglwill have a single<canvas>element. - Export the figure to PNG and compare file sizes: a
Scatterglexport will be small (a few kilobytes) because it’s a raster, whereas an SVG export of the same data would be huge or impossible.
Conclusion
Choosing go.Scattergl over go.Scatter is a pragmatic decision when you need to display large point clouds interactively. The switch is trivial, but remember the trade‑offs: limited styling, no error bars, and rasterized exports. For projects that require full vector fidelity or complex annotations, combine Scattergl for exploration with downsampling or alternative trace types for final output.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.