Chart.js decimation for high-volume line charts
Use Chart.js decimation with LTTB to render large time-series line charts smoothly while keeping raw data for tooltips and export.
14 Sept 2025, 22:14 UTC

The problem: too many points block the main thread
A Chart.js line chart with tens of thousands of time-series points can freeze the browser tab while the chart draws. Every point becomes a draw call—an instruction to render part of the chart—and interactions such as hovering or zooming lag because the main thread is busy processing the full array. If your chart shows server metrics, sensor readings, or financial ticks, you need a rendering strategy that reduces draw calls without discarding the source data.
The practical takeaway is to enable Chart.js decimation with the LTTB algorithm, scale the sample count to the canvas pixel width, and keep the original dataset intact for tooltips, export, and accessibility.
Requirements
- The chart must be a line chart. Decimation is not supported for bar, scatter, or category-based charts.
- The x-axis must use a linear or time scale.
- Data must be sorted by x value. Unsorted data produces misleading gaps and visual artifacts.
- Visual approximation is acceptable. If every individual point must be inspectable, decimation is the wrong tool.
Smallest suitable design
The smallest design is a line chart with the decimation plugin registered, decimation enabled, the LTTB (Largest-Triangle-Three-Buckets) algorithm selected, and samples set to the canvas width in pixels. LTTB reduces the point count while preserving peaks and troughs, so the visual envelope stays faithful to the raw data.
const config = {
type: 'line',
data: {
labels: timeStamps, // sorted by x
datasets: [{
label: 'Sensor reading',
data: readings, // full raw array
showLine: true,
}],
},
options: {
scales: {
x: { type: 'time' },
},
plugins: {
decimation: {
enabled: true,
algorithm: 'lttb',
samples: window.innerWidth * window.devicePixelRatio,
},
},
},
plugins: [decimationPlugin],
};
Set samples to the number of physical pixels across the chart area. Requesting more samples than pixels wastes CPU with no visible benefit. Because canvas scaling varies by display, recalculate this value when the container resizes or when the user changes zoom.
Trust and data boundaries
Decimation is a visual reduction only. The plugin receives the full dataset and draws a smaller subset, but the original array remains the source of truth for tooltips, data export, and accessible alternatives. Do not treat the decimated output as a complete dataset; sending it to a server or using it for calculations would drop points and produce incorrect results.
If the data comes from an untrusted source, validate and sort it before passing it to Chart.js. Decimation assumes sorted x values; unsorted input is a data-quality failure, not a rendering failure.
Operational checks
- Open the chart with 50,000 or more sorted points and confirm rendering feels smoother than with decimation disabled.
- Hover over several points and verify tooltip values match the raw dataset, not the decimated subset.
- Open the browser console while the chart loads. Look for warnings about unsupported scales or unsorted data when decimation is active.
- Compare the decimated visual against the raw data at multiple zoom levels to confirm peaks and troughs are preserved.
Failure modes
- Wrong axis type: Using a category axis causes incorrect decimation because the plugin cannot interpolate between non-numeric x positions.
- Unsorted data: Points out of x-order create misleading gaps or spikes in the rendered line.
- Excessive samples: Setting
sampleshigher than the canvas pixel width burns CPU for no visual gain. - Memory unchanged: Decimation reduces draw calls, but the full array still occupies heap memory. Very large arrays can still pressure the browser.
Conditions that change the design
- If the dataset reaches millions of points, move aggregation to the server or switch to a WebGL-based renderer. Client-side decimation still parses and holds the full array.
- If users require exact per-point inspection, disable decimation and load data progressively or use a smaller time window.
- If the x-axis is categorical, do not use decimation; aggregate on the server instead.
Limitations and verification
Default values for samples and thresholds differ between Chart.js major versions. Confirm the defaults for your installed release before relying on them, and register the plugin explicitly if your build requires it.
To verify the design, create a test chart with 50,000 sorted time-series points. Enable decimation, confirm the visual envelope matches the raw data, then toggle decimation off and compare tooltip values at specific x positions. If tooltips resolve against the original dataset and the console stays clean, the boundary between visual rendering and source data is working as intended.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.