When Plotly SVG Chokes, Switch Your Scatter to WebGL
Plotly’s default scatter uses SVG DOM elements and slows down with tens of thousands of points. Switching to scattergl moves rendering to WebGL for interactive exploration of hundreds of thousands of points, with clear trade-offs in features and payload.
17 Feb 2026, 10:15 UTC

Loading a few hundred thousand points into Plotly and watching zoom and pan turn into a slideshow is a familiar pain. The default scatter trace draws every point as an SVG DOM element. The browser has to manage each element, and interactive performance collapses around tens of thousands of points. The useful takeaway is not to downsample by default, but to move rendering to the GPU with scattergl and keep the full data visible.
SVG vs WebGL rendering in Plotly
Plotly’s default go.Scatter uses SVG. SVG is vector-based and great for crisp styling, per-point text labels, and a wide set of marker symbols. The cost is DOM weight: each point becomes a DOM node the browser tracks for hit testing and transforms.
go.Scattergl uses WebGL. WebGL is a browser API for GPU-accelerated 2D and 3D graphics. Points are uploaded to a GPU buffer and drawn as a single draw call. That changes the bottleneck from DOM management to GPU fill rate and data transfer. Core interactions remain: zoom, pan, and hover tooltips work. The code change is minimal, the rendering backend changes completely.
Opting in with plotly.py
In Graph Objects you swap the trace type. In Plotly Express you set render_mode='webgl'.
import plotly.graph_objects as go
import plotly.express as px
import numpy as np
# Example data shape, run in a Python environment with plotly installed
n = 500_000
x = np.arange(n)
y = np.cumsum(np.random.randn(n))
fig_svg = go.Figure(go.Scatter(x=x, y=y, mode='lines', name='svg'))
fig_gl = go.Figure(go.Scattergl(x=x, y=y, mode='lines', name='webgl'))
# Plotly Express equivalent
# fig_px = px.line(x=x, y=y, render_mode='webgl')
Permissions required are none beyond a standard Python install. Risk: WebGL depends on the viewer’s browser and GPU. Environments without hardware acceleration, some VMs, or old browsers may fall back or fail to render. Feature parity also varies by Plotly version. Check your installed version’s documentation for supported marker symbols, per-point text, and error bars before assuming equivalence.
A reproducible comparison pattern
Build two figures from the same array, one with Scatter and one with Scattergl, and open them side by side in the same browser. Interact with zoom and pan on both. The SVG version will show noticeable lag as point count rises; the WebGL version stays responsive for interactive exploration.
A practical check for whether rendering or payload is your bottleneck:
import json
size_bytes = len(json.dumps(fig_gl.to_dict()))
print(size_bytes)
A large serialized figure means the data travels as JSON to the browser. In Dash apps, dcc.Graph will still send the full arrays over the network even if rendering is fast. If payload dominates, pair WebGL with server-side or client-side downsampling, e.g., aggregating to min/max per pixel bucket, before plotting.
Trade-offs and limits
WebGL handles rendering cost but does not reduce data transfer or memory use. Some SVG-mode features are absent or behave differently in scattergl, such as certain marker symbols, per-point text labels, and some hover styling options. Exact support changes across Plotly releases, so verify capabilities against the version you target rather than assuming full equivalence.
Performance thresholds are rules of thumb, not benchmarks. Actual limits depend on hardware, browser, and trace complexity.
Actionable next step: identify the largest interactive trace in your app, create the scattergl variant, and test it in the browsers and environments your readers actually use, including one without GPU acceleration if relevant. Keep the SVG version for small, highly styled traces where DOM precision matters, and use scattergl for large, exploratory datasets where interactivity matters more than fine-grained styling.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.