Using Plotly's scattergl for Interactive Plots of Large Datasets
Learn when and how to switch Plotly’s default SVG scatter trace to the WebGL‑accelerated scattergl type for smooth interaction with large datasets.
16 Oct 2025, 17:47 UTC

Why SVG traces become a bottleneck
Plotly.js renders default scatter traces as SVG elements. Each data point creates a DOM node, which works well for a few thousand points but starts to lag when you approach tens of thousands. Pan, zoom, and hover interactions feel sluggish because the browser must update many nodes on every mouse move.
When to switch to WebGL‑accelerated traces
If your dataset exceeds roughly 50 k points and you need interactive exploration, consider the scattergl trace type. It draws points with WebGL, leveraging the GPU so that hundreds of thousands or even millions of points remain responsive. The switch is usually a one‑line change: replace type: 'scatter' with type: 'scattergl' in Plotly.js, or use go.Scattergl instead of go.Scatter in Python’s plotly.graph_objects.
Supported features and differences
Most styling options (color, size, opacity) work the same, but some SVG‑specific features are limited or unavailable in WebGL mode:
- Certain marker symbols (e.g., custom paths) may fall back to a default circle.
- Error bars and some hover label configurations are not fully supported.
- Gradient fills and patterns on markers are not available.
Always consult the Plotly figure reference for your installed version to verify which attributes are supported for scattergl.
Worked example: comparing SVG and WebGL rendering
The following Python snippet creates two figures with 500 000 random points. The first uses the default SVG scatter trace; the second uses the WebGL‑accelerated version. Running this code in a Jupyter notebook or a Dash app lets you observe the difference in responsiveness.
import numpy as np
import plotly.graph_objects as go
# Generate a large dataset
rng = np.random.default_rng(42)
x = rng.normal(size=500_000)
y = rng.normal(size=500_000)
# SVG trace (default)
fig_svg = go.Figure()
fig_svg.add_trace(go.Scatter(x=x, y=y, mode='markers', marker=dict(size=2, color='rgba(0,100,250,0.5)')))
fig_svg.update_layout(title='SVG Scatter (500k points)', width=700, height=400)
# WebGL trace
fig_gl = go.Figure()
fig_gl.add_trace(go.Scattergl(x=x, y=y, mode='markers', marker=dict(size=2, color='rgba(0,100,250,0.5)')))
fig_gl.update_layout(title='WebGL Scatter (scattergl) – 500k points', width=700, height=400)
# Show both figures (in a notebook they will appear side‑by‑side)
fig_svg.show()
fig_gl.show()
When you interact with the SVG figure, you may notice delayed zooming or laggy hover tooltips. The WebGL figure should remain smooth, assuming the browser supports WebGL and the GPU has sufficient memory.
Trade‑offs and limitations
While scattergl solves the rendering bottleneck, it introduces other considerations:
- Data transfer: Large arrays are still serialized to JSON (or binary in Dash) before reaching the browser, so network latency can dominate for multi‑million‑point datasets.
- GPU memory: Extremely large point clouds can exceed the graphics card’s memory, causing crashes or fallback to software rendering.
- Export differences: Static image export via Kaleido or
fig.write_imagemay render WebGL traces differently than the on‑screen canvas; verify output if pixel‑perfect export is required. - Feature gaps: As noted, some marker styles, error bars, and hover behaviors are not identical to SVG traces.
A practical mitigation is to downsample or aggregate data before plotting when interactivity is less critical at full resolution, reserving WebGL for exploratory views where every point matters.
Actionable steps for your project
- Profile your current scatter plot with the browser’s developer tools (look for long layout or paint times).
- If interaction feels sluggish and you have >50 k points, try swapping
go.Scatterforgo.Scattergl(or the JSON equivalent). - Check the Plotly reference for your version to confirm that the marker, hover, and error‑bar attributes you need are supported.
- Test static export with your target tool (Kaleido, orca, etc.) to ensure the visual output matches expectations.
- Monitor GPU usage in the browser’s task manager; if memory spikes, consider a downstream downsampling step.
By making this small change and validating the trade‑offs, you can keep Plotly visualizations interactive even as your datasets grow into the hundreds of thousands or millions of points.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.