Direct Answer
Plotly.js does not automatically redraw after a WebGL context loss. You must capture the current view state (axis ranges) during the plotly_webglcontextlost event, then call Plotly.react with the same data, layout, and captured ranges on plotly_webglcontextrestored. Interaction state (hover, selection) cannot be restored automatically; cache the relevant point indices and reapply them with Plotly.restyle. There is no built‑in render queue—implement a simple flag or state machine to suppress redundant render calls while the context is lost. For large datasets, Plotly.react is significantly faster than Plotly.newPlot and avoids memory leaks.
Re‑initialization Pattern Preserving View State
- Attach listeners to the plot’s DOM element for
plotly_webglcontextlost and plotly_webglcontextrestored.
- On
contextlost, read layout.xaxis.range and layout.yaxis.range (and any other view‑dependent layout properties) and store them in a module‑level variable.
- Set a
contextLost flag to true to block any pending Plotly.react or Plotly.restyle calls.
- On
contextrestored, merge the saved ranges into a fresh layout object, call Plotly.react(gd, data, layout, config), then clear the flag.
- If you need to restore hover or selection, cache the point indices (or
curveNumber/pointNumber) before the loss and call Plotly.restyle(gd, {selectedpoints: cachedIndices}) after the react completes.
Performance Mitigation for Large Datasets
- Use
Plotly.react instead of Plotly.newPlot – it updates the existing DOM node and reuses WebGL buffers where possible, reducing GC pressure.
- Simplify marker symbols – complex SVG symbols fall back to canvas rendering and increase restore time. Stick to WebGL‑native symbols (circle, square, diamond, cross, x).
- Throttle restoration – if context loss fires repeatedly (e.g., GPU memory pressure), debounce the
contextrestored handler (e.g., 500 ms) to avoid thrashing the tab.
- Reduce data precision – for 100k+ points, consider downsampling or using
gl-matrix typed arrays (Float32Array) for coordinates to lower GPU memory footprint.
Render Queue: Build Your Own
Plotly.js provides no queuing mechanism. A minimal implementation:
let contextLost = false;
const pendingRenders = [];
function safeReact(gd, data, layout, config) {
if (contextLost) {
pendingRenders.push({data, layout, config});
return;
}
Plotly.react(gd, data, layout, config);
}
plotDiv.on('plotly_webglcontextlost', () => { contextLost = true; });
plotDiv.on('plotly_webglcontextrestored', () => {
contextLost = false;
while (pendingRenders.length) {
const {data, layout, config} = pendingRenders.shift();
Plotly.react(plotDiv, data, layout, config);
}
});
Verification Steps
- Open Chrome DevTools → WebGL tab (or use
chrome://gpu) and trigger "Lose context" to simulate loss.
- Confirm
plotly_webglcontextlost and plotly_webglcontextrestored fire on the plot div.
- Measure
Plotly.react vs Plotly.newPlot restore time for a 150k‑point scattergl trace using console.time.
- Verify that axis ranges and selection indices match pre‑loss state after restoration.
Assumptions & Uncertainties
- Tested with Plotly.js 2.x (current as of 2026). Behavior may differ in 1.x.
- Marker symbol fallback is GPU/driver dependent; no guaranteed cross‑vendor consistency.
- Frequent context loss (<5 s intervals) may still crash the tab despite throttling—consider reducing dataset size or switching to SVG scatter for critical views.