Interactive uPlot-versus-library comparisons: LTTB, averaging, grouping, and min/max alternatives.
These examples illustrate the LTTB limitations discussed in #1025.
The green uPlot reference charts use all 10,002 input samples with the default linear pathbuilder. Its pixel-column min/max decimation retains both the +100 and -100 spikes.
In the uniform example, the spikes are five samples apart. In the non-uniform example, 9,981 samples occupy the first 10% of the x range.
The static comparisons below demonstrate LTTB losses with the native ECharts and Chart.js implementations. No uPlot chart uses LTTB, and this demo contains no custom LTTB implementation. This compares visible features, not equal output sizes or execution times.
These charts use the ECharts and Chart.js versions pinned in the benchmark dependency manifest. The examples require network access to load these libraries.
Each pair compares all input samples in green with native LTTB selection in red. Both charts receive the same 10,002 input samples. The output under each chart reports the actual retained samples and spike counts.
LTTB is opt-in in both libraries. This demo uses ECharts sampling: lttb. The benchmark uses minmax instead.
Its default sampling budget depends on the plot width and browser pixel ratio. The grid and pixel ratio use library defaults.
Chart.js uses enabled: true, algorithm: lttb with parsing: false, which its decimation plugin requires.
The output target defaults to the chart width in CSS pixels. The activation threshold defaults to four times that width.
Neither value is overridden. The Chart.js benchmark instead uses the default min-max algorithm.
These are default LTTB sampling settings, not completely default charts. The demo fixes chart sizes and axis limits, disables animation and point symbols, and sets line colors and widths for comparison. ECharts uses sample indices for triangle geometry. Chart.js uses actual x values, so their selected samples need not match.
The results depend on chart size, library version, and pixel ratio. The readouts report the actual selected samples and spike counts. Retaining both spikes does not guarantee their original shape.
Smaller buckets mean more output samples and can recover a missing feature in a particular dataset. Changing the sample count also changes bucket boundaries, so recovery is not a general preservation guarantee. Unknown or changing data can require a different budget. Retaining all samples removes the selection loss, but also removes the benefit of downsampling.
In Chart.js, lowering threshold activates decimation for smaller datasets. It does not make the LTTB buckets smaller.
Increasing samples requests more output points. ECharts derives its LTTB budget from the plot width and pixel ratio.
The detail canvases magnify the actual output polylines without another sampling pass. Normal chart zoom can change buckets or disable downsampling.
ECharts: waiting for library.
Chart.js: waiting for library.
The spikes occur at indices 5005 and 5010. If both occupy one LTTB bucket, only one can survive.
Detail: x = 4980…5060
Detail: x = 4980…5060, no resampling
Detail: x = 4980…5060
Detail: x = 4980…5060, no resampling
The final 90% of the x range contains only 21 samples. Count-based buckets allocate few output points to this wide region.
Both spikes have value +100, at indices 5005 and 5025. Depending on the sampling budget, they can occupy different buckets. After the first spike, LTTB can prefer a baseline point over the second spike. One peak per bucket is not a preservation guarantee.
Detail: x = 4980…5060
Detail: x = 4980…5060, no resampling
Detail: x = 4980…5060
Detail: x = 4980…5060, no resampling
LTTB selects existing data points, but the lines between them can misrepresent the original signal. Pixel-column min/max preserves local extrema, not every subpixel detail or signal statistic.
Implementation sources: ECharts LTTB, ECharts sampling rate, Chart.js decimation.