This benchmark suite compares the rendering performance of popular JavaScript charting libraries under extreme workloads:
- many series
- different series types (line, scatter, column, mountain, area, candlestick, heatmap, 3D surface, 3D point cloud)
- millions of data-points
- real-time streaming and data ingestion tests
- multiple charts on screen (up to 128 charts)
- heatmaps and 3D charts
- GPU-first architectures scale orders of magnitude further than CPU / Canvas / SVG-based libraries.
- Libraries designed for general-purpose dashboards degrade rapidly under large datasets or high update rates.
- Sustained 60 FPS at large scale is only achievable with WebGL-based rendering and optimized data pipelines.
This Test suite performs JavaScript Chart stress tests and compares the following libraries:
- SciChart.js
- HighCharts (with Boost module)
- Plotly.js (with GL series types where available)
- Chart.js
- Apache eCharts (with GL series types where available)
- uPlot
- ChartGPU
- LightningChart (LCJS)
The test suite records a number of metrics which are stored in IndexedDB. These can be run as a batch (see Playwright instructions below) and imported, or run one by one via the UI.
- FPS is measured visually and via requestAnimationFrame where applicable.
- Initialisation Time is measured as the time from start of the test to first render (time to first render, milliseconds)
- Memory is recorded via
performance.memory.usedJSHeapSize. This is only available in the Chromium browsers - Data ingestion rate is calculated on a per-test basis: e.g. for static tests Data Ingestion Rate = numberOfPoints / timeToFirstRender. For dynamic tests, its numberOfPointsPerFrame * FPS.
- Total frames is output as a metric
Notes:
- Some libraries may report high rAF rates while rendering visually lags or other large delays on initialisation, e.g. Chart.js.
- Browser crashes, hangs, or skipped tests are considered failures.
- All test results are logged to IndexedDB and displayed on the homepage (refresh page to view)
- It does not measure aesthetics, API ergonomics, or learning curve.
- It does not represent typical dashboard workloads.
- It does not imply open source or smaller libraries are "bad" — only that they are not designed for extreme scale.
- Results should not be extrapolated to SVG or static charts (draw once / no interaction use-cases)
This test suite aims to test a variety of JavaScript Chart operations with a variety of test cases, including Line, Scatter, Column, Candlestick, Heatmap, as well as 3D Chart (Surface mesh/plot, 3D point cloud) and multi-chart cases.
A full list of test cases carried out and their descriptions can be found below:
Multi-line test case for monte carlo simulation style charts. Starting with 100 line series x 100 data-points per series, the test is incrementally updated to 200x200, 500x500, 1000x1000, 2000x2000 and 4000x4000.
This test case stresses the static overhead of adding a line series and drawing to a chart, while dynamically varying the zoom to measure the update rate.
Single chart, single series test with a randomized, Xy data set rendered by scatter points. Starting at 1000 datapoints, the point-count is incrementally updated to 10000, 50000, 100000, 200000, 500000 all the way up to 10 million data-points. The dataset is updated in realtime and the chart render speed, memory and frame count is measured.
This test case stresses the real-time data update rate of the chart for randomised data when rendering scatter plots. As no caching and no optimisations can be enabled for random data, this test stresses the raw drawing performance of the chart.
Single chart, single series test with a randomized, Xy data set rendered by line points. Starting at 1000 datapoints, the point-count is incrementally updated to 10000, 50000, 100000, 200000, 500000 all the way up to 10 million data-points. The dataset is updated in realtime and the chart render speed, memory and frame count is measured.
This test case stresses the real-time data update rate of the chart for randomised data when rendering line plots. As no caching and no optimisations can be enabled for random data, this test stresses the raw drawing performance of the chart.
With x-values sorted ascending, some caching optimisations can be enabled, however with randomised data, this test stresses the data update rate of the chart to draw lines and scatter points simultaneously, for incrementally increasing point-counts from 1000 points through to 10 million datapoints.
Stresses the rendering performance of column or bar charts, an often overlooked chart type in high performance visualisation, but one that is critical in dashboards and complex applications.
A static dataset is loaded and the chart programatically zoomed to measure the redraw rate of the chart. This test stresses rendering performance, but not data update rate.
Stresses the rendering performance of candlestick charts, an often overlooked chart type in high performance financial visualisation, but one that is critical in financial applications, quantitative trading and HFT applications.
A static dataset is loaded and the chart programatically zoomed to measure the redraw rate of the chart. This test stresses rendering performance, but not data update rate.
A single chart is loaded with 5 series, each with a fixed number of data-points. New data is appended in realtime and the chart scrolled in a 'First in first out' or ECG style. Test cases get incrementally harder starting off at hundreds of data-points per second and ramping up to millions of data-points per second ingested.
This test case stresses the data update rate and rendering capabilities of the charts, giving an indication of the datapoints per second that can realistically be sent to a JavaScript chart under these conditions.
Mountain or area charts with static data where the chart is programmatically zoomed
Realtime charts where data is appended to a line chart as fast as you can
An increasing numbers of charts (2, 4, 8, 16 ... up to 128 charts) each with realtime line series
Realtime uniform heatmap updating as fast as possible with increasing number of cells
Realtime 3D point clouds with randomised data, with increasing numbers of data-points
Realtime 3D surface plots with a generated sinusoidal function with increasing number of cells
with the following hardware, these test results are achieved:
User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36
GPU Vendor: Google Inc. (NVIDIA)
GPU Renderer: ANGLE (NVIDIA, NVIDIA GeForce RTX 4090 Laptop GPU (0x00002717) Direct3D11 vs_5_0 ps_5_0, D3D11)
ANGLE: Yes (ANGLE)
Platform: Win32
CPU: Intel Core i9-14900HX (2.2GHz)
Hardware Concurrency: 32 cores
Video Memory: 16 GB
SciChart.js scored the highest FPS in 8 out of 9 test configurations, with ChartGPU taking 1 win at the smallest data size (1,000 points). SciChart.js maintained over 237 FPS up to 200,000 points. At 50,000 points, HighCharts (20.00 FPS), Chart.js (2.08 FPS), eCharts and uPlot did not complete the test at this level, while Plotly.js (87.58 FPS), ChartGPU (199.55 FPS) and LCJS v8 (196.33 FPS) remained competitive at that scale. At 10 million points, SciChart.js recorded 5.39 FPS -- approximately 41% faster than LCJS v8 (3.81 FPS) and 7x faster than ChartGPU (0.76 FPS). At 1 million points, SciChart.js (59.33 FPS) was approximately 55% faster than LCJS v8 (38.40 FPS), 17x faster than HighCharts (3.46 FPS) and 26x faster than Plotly.js (2.31 FPS).
| Fastest | Parameters | SciChart.js (FPS) | HighCharts (FPS) | Chart.js (FPS) | Plotly.js (FPS) | eCharts (FPS) | uPlot (FPS) | ChartGPU (FPS) | LCJS v8 (FPS) |
|---|---|---|---|---|---|---|---|---|---|
| ChartGPU | 1,000 points, 1 series | 236.99 | 71.75 | 80.78 | 147.05 | 210.21 | 196.68 | 237.84 | 224.58 |
| SciChart.js | 10,000 points, 1 series | 239.32 | 48.94 | 9.91 | 132.32 | 44.41 | 10.73 | 239.11 | 234.69 |
| SciChart.js | 50,000 points, 1 series | 239.21 | 20.00 | 2.08 | 87.58 | Hanging | Hanging | 199.55 | 196.33 |
| SciChart.js | 100,000 points, 1 series | 239.37 | 13.31 | 1.06 | 23.92 | Skipped | Skipped | 106.22 | 131.13 |
| SciChart.js | 200,000 points, 1 series | 237.52 | 10.54 | 0.50 | 10.54 | Skipped | Skipped | 41.09 | 97.84 |
| SciChart.js | 500,000 points, 1 series | 98.71 | 6.25 | Skipped | 4.54 | Skipped | Skipped | 15.94 | 67.14 |
| SciChart.js | 1,000,000 points, 1 series | 59.33 | 3.46 | Skipped | 2.31 | Skipped | Skipped | 6.80 | 38.40 |
| SciChart.js | 5,000,000 points, 1 series | 10.54 | 0.89 | Skipped | 0.37 | Skipped | Skipped | 1.57 | 7.32 |
| SciChart.js | 10,000,000 points, 1 series | 5.39 | Skipped | Skipped | Skipped | Skipped | Skipped | 0.76 | 3.81 |
SciChart.js scored the highest FPS in all 9 out of 9 test configurations. This test renders polylines with unsorted XY data, which is particularly demanding on rendering engines. SciChart.js maintained over 236 FPS through 50,000 points and sustained 50.31 FPS at 1 million points. At 10 million points, SciChart.js recorded 2.86 FPS -- approximately 4x faster than LCJS v8 (0.66 FPS) and 5x faster than ChartGPU (0.53 FPS). At 1 million points, SciChart.js (50.31 FPS) was approximately 5x faster than LCJS v8 (9.99 FPS), 10x faster than ChartGPU (4.99 FPS) and 16x faster than HighCharts (3.18 FPS). Apache eCharts and uPlot both reported very low FPS at 50,000 points (0.21 and 0.11 respectively) and did not continue beyond 100,000 points. Chart.js did not continue beyond 50,000 points.
| Fastest | Parameters | SciChart.js (FPS) | HighCharts (FPS) | Chart.js (FPS) | Plotly.js (FPS) | eCharts (FPS) | uPlot (FPS) | ChartGPU (FPS) | LCJS v8 (FPS) |
|---|---|---|---|---|---|---|---|---|---|
| SciChart.js | 1,000 points, 1 series | 236.96 | 55.40 | 73.57 | 133.62 | 62.36 | 52.64 | 235.87 | 224.06 |
| SciChart.js | 10,000 points, 1 series | 237.37 | 50.26 | 3.37 | 113.66 | 3.77 | 3.77 | 231.68 | 220.41 |
| SciChart.js | 50,000 points, 1 series | 236.85 | 26.39 | 0.26 | 58.66 | 0.21 | 0.11 | 93.34 | 162.51 |
| SciChart.js | 100,000 points, 1 series | 236.76 | 16.89 | Skipped | 17.46 | Skipped | Skipped | 45.53 | 87.89 |
| SciChart.js | 200,000 points, 1 series | 194.96 | 12.19 | Skipped | 8.18 | Skipped | Skipped | 21.97 | 46.91 |
| SciChart.js | 500,000 points, 1 series | 89.43 | 5.86 | Skipped | 2.92 | Skipped | Skipped | 9.27 | 19.33 |
| SciChart.js | 1,000,000 points, 1 series | 50.31 | 3.18 | Skipped | 1.30 | Skipped | Skipped | 4.99 | 9.99 |
| SciChart.js | 5,000,000 points, 1 series | 9.39 | 0.81 | Skipped | Hanging | Skipped | Skipped | 1.20 | 1.80 |
| SciChart.js | 10,000,000 points, 1 series | 2.86 | Skipped | Skipped | Skipped | Skipped | Skipped | 0.53 | 0.66 |
SciChart.js scored the highest FPS in 7 out of 9 test configurations, with ChartGPU taking 2 wins at the smallest data sizes (1,000 and 10,000 points). This test measures the performance of updating Y-values on a sorted point series. SciChart.js maintained over 232 FPS through 100,000 points and recorded 62.94 FPS at 1 million points. At 10 million points, SciChart.js recorded 4.82 FPS -- approximately 5x faster than both ChartGPU (0.95 FPS) and LCJS v8 (0.91 FPS). At 1 million points, SciChart.js (62.94 FPS) was approximately 5x faster than LCJS v8 (13.11 FPS), 8x faster than ChartGPU (8.22 FPS) and 18x faster than HighCharts (3.48 FPS). eCharts did not continue beyond 50,000 points, and uPlot did not complete the test at that threshold.
| Fastest | Parameters | SciChart.js (FPS) | HighCharts (FPS) | Chart.js (FPS) | Plotly.js (FPS) | eCharts (FPS) | uPlot (FPS) | ChartGPU (FPS) | LCJS v8 (FPS) |
|---|---|---|---|---|---|---|---|---|---|
| ChartGPU | 1,000 points, 1 series | 232.98 | 74.86 | 53.40 | 151.60 | 47.02 | 57.99 | 237.76 | 227.58 |
| ChartGPU | 10,000 points, 1 series | 235.00 | 58.80 | 9.72 | 114.92 | 5.80 | 12.79 | 238.45 | 213.43 |
| SciChart.js | 50,000 points, 1 series | 235.87 | 27.16 | 1.79 | 60.23 | 0.63 | Hanging | 179.31 | 231.88 |
| SciChart.js | 100,000 points, 1 series | 234.77 | 17.59 | 0.93 | 23.65 | Skipped | Skipped | 111.27 | 150.75 |
| SciChart.js | 200,000 points, 1 series | 205.01 | 12.64 | Skipped | 11.19 | Skipped | Skipped | 48.18 | 79.47 |
| SciChart.js | 500,000 points, 1 series | 94.45 | 6.67 | Skipped | 4.24 | Skipped | Skipped | 17.26 | 27.99 |
| SciChart.js | 1,000,000 points, 1 series | 62.94 | 3.48 | Skipped | 1.95 | Skipped | Skipped | 8.22 | 13.11 |
| SciChart.js | 5,000,000 points, 1 series | 11.69 | 0.90 | Skipped | 0.37 | Skipped | Skipped | 1.81 | 2.25 |
| SciChart.js | 10,000,000 points, 1 series | 4.82 | Skipped | Skipped | Skipped | Skipped | Skipped | 0.95 | 0.91 |
SciChart.js scored the highest FPS in all 9 out of 9 test configurations. This test measures static rendering of column/bar charts. SciChart.js maintained approximately 235–239 FPS across all data sizes up to 10 million points, demonstrating that its column rendering performance is nearly independent of data volume. At 10 million points, SciChart.js (237.03 FPS) was approximately 239x faster than ChartGPU (0.99 FPS), the only other library that could render at that scale. LCJS v8 performed well through 200,000 points (215.90 FPS) but did not complete the test at 500,000 points. At 1 million points, SciChart.js (238.79 FPS) was approximately 14x faster than ChartGPU (17.26 FPS) and 80x faster than HighCharts (3.00 FPS). Chart.js did not continue beyond 100,000 points, Plotly.js did not continue beyond 100,000 points, and Apache eCharts encountered an error at 200,000 points and did not continue beyond that threshold.
| Fastest | Parameters | SciChart.js (FPS) | HighCharts (FPS) | Chart.js (FPS) | Plotly.js (FPS) | eCharts (FPS) | uPlot (FPS) | ChartGPU (FPS) | LCJS v8 (FPS) |
|---|---|---|---|---|---|---|---|---|---|
| SciChart.js | 1,000 points, 1 series | 235.55 | 57.52 | 127.22 | 83.97 | 20.79 | 231.03 | 235.13 | 224.11 |
| SciChart.js | 10,000 points, 1 series | 238.56 | 60.97 | 19.80 | 9.15 | 95.29 | 166.52 | 218.53 | 234.57 |
| SciChart.js | 50,000 points, 1 series | 239.09 | 21.56 | 3.80 | 1.09 | 91.42 | 32.96 | 184.87 | 206.34 |
| SciChart.js | 100,000 points, 1 series | 239.14 | 13.77 | 1.75 | 0.31 | 34.12 | 15.52 | 115.12 | 131.22 |
| SciChart.js | 200,000 points, 1 series | 239.36 | 9.97 | Skipped | Skipped | Error | 6.57 | 70.48 | 215.90 |
| SciChart.js | 500,000 points, 1 series | 239.44 | 6.10 | - | Skipped | Skipped | 2.17 | 35.19 | Hanging |
| SciChart.js | 1,000,000 points, 1 series | 238.79 | 3.00 | - | Skipped | Skipped | 0.82 | 17.26 | Skipped |
| SciChart.js | 5,000,000 points, 1 series | 238.23 | 0.80 | - | Skipped | Skipped | Skipped | 2.12 | Skipped |
| SciChart.js | 10,000,000 points, 1 series | 237.03 | Skipped | - | Skipped | Skipped | Skipped | 0.99 | Skipped |
SciChart.js scored the highest FPS in 7 out of 9 test configurations, with ChartGPU taking 2 wins at the smallest data sizes (1,000 and 10,000 points). Candlesticks are complex geometry (box with outline and high/low wick), making this a demanding test for rendering engines. SciChart.js maintained over 228 FPS across all data sizes up to 10 million candles. At 10 million points, SciChart.js (228.04 FPS) was approximately 42x faster than ChartGPU (5.49 FPS), the only other library that could render at that scale. At 1 million points, SciChart.js (234.66 FPS) was approximately 5x faster than ChartGPU (47.02 FPS) and 21x faster than LCJS v8 (10.94 FPS). HighCharts did not complete the test at 1,000 points and did not continue thereafter. Plotly.js did not complete the test at 50,000 points, Chart.js did not continue beyond 50,000 points, and uPlot did not continue beyond 500,000 points. LCJS v8 performed well through 200,000 points (141.16 FPS) but did not complete the test at 5 million points. Apache eCharts did not complete the test at 5 million points and did not continue beyond that.
| Fastest | Parameters | SciChart.js (FPS) | HighCharts (FPS) | Chart.js (FPS) | Plotly.js (FPS) | eCharts (FPS) | uPlot (FPS) | ChartGPU (FPS) | LCJS v8 (FPS) |
|---|---|---|---|---|---|---|---|---|---|
| ChartGPU | 1,000 points, 1 series | 236.08 | Hanging | 49.78 | 26.15 | 230.10 | 230.22 | 236.27 | 224.30 |
| ChartGPU | 10,000 points, 1 series | 236.18 | Skipped | 7.89 | 2.17 | 139.48 | 59.85 | 238.14 | 217.39 |
| SciChart.js | 50,000 points, 1 series | 234.73 | Skipped | 0.91 | Hanging | 46.46 | 13.90 | 232.73 | 193.82 |
| SciChart.js | 100,000 points, 1 series | 234.84 | Skipped | Skipped | Skipped | 28.32 | 6.63 | 189.85 | 146.85 |
| SciChart.js | 200,000 points, 1 series | 233.60 | Skipped | Skipped | Skipped | 15.97 | 2.86 | 139.19 | 141.16 |
| SciChart.js | 500,000 points, 1 series | 234.46 | Skipped | Skipped | Skipped | 7.65 | 0.89 | 77.61 | 37.64 |
| SciChart.js | 1,000,000 points, 1 series | 234.66 | Skipped | Skipped | Skipped | 3.75 | Skipped | 47.02 | 10.94 |
| SciChart.js | 5,000,000 points, 1 series | 232.69 | Skipped | Skipped | Skipped | Hanging | Skipped | 10.89 | Hanging |
| SciChart.js | 10,000,000 points, 1 series | 228.04 | Skipped | Skipped | Skipped | Skipped | Skipped | 5.49 | Skipped |
SciChart.js scored the highest FPS in 7 out of 9 test configurations, with uPlot taking 2 wins at 50,000 and 200,000 points. This test measures mountain (area) chart rendering performance. SciChart.js, LCJS v8, and uPlot all showed strong performance at moderate data sizes, with all three exceeding 230 FPS at 10,000 points. Plotly.js also performed well through 1 million points (126.19 FPS) but did not complete the test at 5 million points. At 10 million points, SciChart.js (236.00 FPS) was approximately 7% faster than LCJS v8 (220.89 FPS) and 15x faster than uPlot (16.21 FPS). Chart.js did not continue beyond 50,000 points, and Apache eCharts did not continue beyond 1 million points.
| Fastest | Parameters | SciChart.js (FPS) | HighCharts (FPS) | Chart.js (FPS) | Plotly.js (FPS) | eCharts (FPS) | uPlot (FPS) | ChartGPU (FPS) | LCJS v8 (FPS) |
|---|---|---|---|---|---|---|---|---|---|
| SciChart.js | 1,000 points, 1 series | 238.31 | 206.32 | 44.02 | 226.12 | 197.73 | 231.06 | 237.57 | 219.77 |
| SciChart.js | 10,000 points, 1 series | 239.28 | 63.32 | 5.30 | 222.98 | 94.54 | 239.18 | 239.25 | 226.17 |
| uPlot | 50,000 points, 1 series | 238.79 | 19.17 | 1.04 | 217.65 | 30.40 | 239.22 | 178.22 | 236.25 |
| SciChart.js | 100,000 points, 1 series | 237.53 | 12.20 | 0.51 | 199.57 | 15.65 | 235.97 | 105.11 | 232.25 |
| uPlot | 200,000 points, 1 series | 236.18 | 8.37 | Skipped | 172.56 | 6.97 | 236.41 | 52.00 | 232.68 |
| SciChart.js | 500,000 points, 1 series | 239.53 | 4.34 | Skipped | 175.57 | 2.26 | 189.43 | 23.29 | 234.72 |
| SciChart.js | 1,000,000 points, 1 series | 239.39 | 2.56 | Skipped | 126.19 | 0.86 | 119.94 | 12.52 | 233.35 |
| SciChart.js | 5,000,000 points, 1 series | 237.01 | 0.50 | Skipped | Hanging | Skipped | 30.50 | 2.13 | 228.01 |
| SciChart.js | 10,000,000 points, 1 series | 236.00 | Skipped | Skipped | Skipped | Skipped | 16.21 | 0.62 | 220.89 |
This test measures first-in-first-out streaming with 5 line series, the type of chart used in real-time telemetry or ECG monitoring. LCJS v8 scored the highest FPS in 3 out of 6 test configurations (at 1 million, 5 million and 10 million points), SciChart.js scored fastest in 2 configurations (10,000 and 100,000 points), and ChartGPU took 1 win at the smallest data size (100 points).
At 10 million points, LCJS v8 (31.50 FPS) was approximately 62% faster than SciChart.js (19.40 FPS). Both libraries significantly outperformed the field at this scale: uPlot recorded 1.25 FPS while all other libraries did not continue. At 100,000 points, SciChart.js (236.14 FPS) and LCJS v8 (235.78 FPS) were nearly identical, both well ahead of uPlot (97.92 FPS) and ChartGPU (58.92 FPS). Chart.js did not complete the test at 100,000 points, HighCharts and Apache eCharts did not complete the test at 1 million points, and ChartGPU encountered an error at 5 million points.
| Fastest | Parameters | SciChart.js (FPS) | HighCharts (FPS) | Chart.js (FPS) | Plotly.js (FPS) | eCharts (FPS) | uPlot (FPS) | ChartGPU (FPS) | LCJS v8 (FPS) |
|---|---|---|---|---|---|---|---|---|---|
| ChartGPU | 100 points, 5 series | 235.13 | 67.17 | 102.14 | 106.29 | 212.86 | 230.72 | 237.66 | 222.02 |
| SciChart.js | 10,000 points, 5 series | 235.69 | 18.82 | 17.35 | 62.77 | 17.08 | 184.10 | 230.87 | 234.71 |
| SciChart.js | 100,000 points, 5 series | 236.14 | 3.92 | Hanging | 5.75 | 1.62 | 97.92 | 58.92 | 235.78 |
| LCJS v8 | 1,000,000 points, 5 series | 79.21 | Hanging | Skipped | 0.61 | Hanging | 12.15 | 6.64 | 81.35 |
| LCJS v8 | 5,000,000 points, 5 series | 29.09 | Skipped | Skipped | Skipped | Skipped | 2.56 | Error | 31.53 |
| LCJS v8 | 10,000,000 points, 5 series | 19.40 | Skipped | Skipped | Skipped | Skipped | 1.25 | Skipped | 31.50 |
This test evaluates how libraries perform when data is incrementally appended and the chart must compress/aggregate the visible series. LCJS v8 scored the highest FPS in 2 out of 5 test configurations (100,000 and 1 million points), while SciChart.js took 2 wins at 1,000 and 10,000 points. At 10 million points, SciChart.js (21.84 FPS) narrowly edged LCJS v8 (20.91 FPS), with uPlot (6.18 FPS) and HighCharts (0.38 FPS) also completing the test. ChartGPU encountered an error at 10 million points and Plotly.js did not complete the test at that scale.
At 1 million points, LCJS v8 (155.83 FPS) was approximately 2.2x faster than SciChart.js (69.38 FPS), 6x faster than uPlot (27.32 FPS) and 8x faster than ChartGPU (20.61 FPS). Chart.js did not continue beyond 1 million points, and Apache eCharts did not continue beyond 1 million points.
| Fastest | Parameters | SciChart.js (FPS) | HighCharts (FPS) | Chart.js (FPS) | Plotly.js (FPS) | eCharts (FPS) | uPlot (FPS) | ChartGPU (FPS) | LCJS v8 (FPS) |
|---|---|---|---|---|---|---|---|---|---|
| SciChart.js | 1,000 points, 1 series | 238.16 | 85.75 | 23.18 | 114.65 | 86.79 | 231.82 | 236.81 | 228.12 |
| SciChart.js | 10,000 points, 1 series | 239.14 | 35.63 | 7.19 | 46.00 | 26.88 | 218.36 | 173.78 | 237.86 |
| LCJS v8 | 100,000 points, 1 series | 173.61 | 13.98 | 1.84 | 11.89 | 6.17 | 87.83 | 68.25 | 226.50 |
| LCJS v8 | 1,000,000 points, 1 series | 69.38 | 3.13 | 0.35 | 2.34 | 0.63 | 27.32 | 20.61 | 155.83 |
| SciChart.js | 10,000,000 points, 1 series | 21.84 | 0.38 | Skipped | Hanging | Skipped | 6.18 | Error | 20.91 |
This test measures how libraries scale with multiple charts on screen simultaneously, showing a mixture of real-time and static data in line, column, mountain, and scatter series, each with 100,000 data points. SciChart.js scored the highest FPS in 6 out of 8 test configurations, with LCJS v8 taking 2 wins at 1 and 2 charts.
At 32 charts, SciChart.js (26.78 FPS) was approximately 6x faster than ChartGPU (4.70 FPS) and 56x faster than HighCharts (0.48 FPS). LCJS v8 did not complete the test at 32 charts. SciChart.js was the only library able to render 64 and 128 charts, recording 12.46 FPS and 7.90 FPS respectively. Apache eCharts and uPlot did not complete the test at 2 charts, Chart.js did not continue beyond 2 charts, and Plotly.js did not continue beyond 4 charts.
| Fastest | Parameters | SciChart.js (FPS) | HighCharts (FPS) | Chart.js (FPS) | Plotly.js (FPS) | eCharts (FPS) | uPlot (FPS) | ChartGPU (FPS) | LCJS v8 (FPS) |
|---|---|---|---|---|---|---|---|---|---|
| LCJS v8 | 100,000 points, 1 series, 1 chart | 155.75 | 13.53 | 1.84 | 11.04 | 4.85 | 79.31 | 65.65 | 220.39 |
| LCJS v8 | 100,000 points, 1 series, 2 charts | 124.67 | 7.57 | 0.77 | 8.41 | Hanging | Hanging | 51.15 | 166.78 |
| SciChart.js | 100,000 points, 1 series, 4 charts | 103.76 | 3.30 | Skipped | 0.54 | Skipped | Skipped | 33.16 | 71.55 |
| SciChart.js | 100,000 points, 1 series, 8 charts | 71.09 | 1.74 | Skipped | Skipped | Skipped | Skipped | 18.31 | 31.45 |
| SciChart.js | 100,000 points, 1 series, 16 charts | 42.75 | 1.08 | Skipped | Skipped | Skipped | Skipped | 7.95 | 10.60 |
| SciChart.js | 100,000 points, 1 series, 32 charts | 26.78 | 0.48 | Skipped | Skipped | Skipped | Skipped | 4.70 | Hanging |
| SciChart.js | 100,000 points, 1 series, 64 charts | 12.46 | Skipped | Skipped | Skipped | Skipped | Skipped | Skipped | Skipped |
| SciChart.js | 100,000 points, 1 series, 128 charts | 7.90 | Skipped | Skipped | Skipped | Skipped | Skipped | - | Skipped |
This test measures how libraries handle increasing numbers of series, each with a correspondingly increasing number of points. Plotly.js scored the highest FPS in 4 out of 7 test configurations (200–2,000 series), LCJS v8 took 1 win (4,000 series), and SciChart.js took 2 wins (100 and 8,000 series).
Plotly.js and LCJS v8 showed strong mid-range performance but both did not complete the test at higher series counts. At 4,000 series x 4,000 points, LCJS v8 (15.49 FPS) was approximately 60% faster than SciChart.js (9.67 FPS) — the only two libraries able to render at that scale. However, SciChart.js was the only library able to render 8,000 series x 8,000 points (2.19 FPS). HighCharts did not complete the test at 200 series, Chart.js did not continue beyond 1,000 series, and Apache eCharts did not complete the test at 2,000 series. ChartGPU did not complete the test at 4,000 series.
| Fastest | Parameters | SciChart.js (FPS) | HighCharts (FPS) | Chart.js (FPS) | Plotly.js (FPS) | eCharts (FPS) | uPlot (FPS) | ChartGPU (FPS) | LCJS v8 (FPS) |
|---|---|---|---|---|---|---|---|---|---|
| SciChart.js | 100 points, 100 series | 235.30 | 41.76 | 10.48 | 225.46 | 82.97 | 183.49 | 212.93 | 224.70 |
| Plotly.js | 200 points, 200 series | 235.92 | Hanging | 2.77 | 238.77 | 36.69 | 43.16 | 120.22 | 231.64 |
| Plotly.js | 500 points, 500 series | 129.03 | Skipped | 0.47 | 148.25 | 7.86 | 6.10 | 43.36 | 134.99 |
| Plotly.js | 1,000 points, 1,000 series | 63.71 | Skipped | Skipped | 94.28 | 1.88 | 1.52 | 18.17 | 78.70 |
| Plotly.js | 2,000 points, 2,000 series | 27.43 | Skipped | Skipped | 49.14 | Hanging | 0.29 | 5.77 | 39.65 |
| LCJS v8 | 4,000 points, 4,000 series | 9.67 | Skipped | Skipped | Hanging | Skipped | Skipped | Hanging | 15.49 |
| SciChart.js | 8,000 points, 8,000 series | 2.19 | Skipped | Skipped | Skipped | Skipped | Skipped | Skipped | Hanging |
This test updates a 2D uniform heatmap with a colour map in real time. Chart.js, uPlot, and ChartGPU do not support heatmaps and were excluded. SciChart.js scored the highest FPS in 5 out of 7 test configurations (the 16,000 grid test was not completed by any library), with LCJS v8 taking 2 wins at the largest renderable sizes (4,000 and 8,000 grid points).
SciChart.js maintained over 235 FPS for small heatmaps (100–200 grid size) and recorded 39.97 FPS at 1,000 grid size. At 8,000 grid points, LCJS v8 (0.64 FPS) was approximately 33% faster than SciChart.js (0.48 FPS). Plotly.js did not complete the test at 4,000 grid points, and HighCharts and Apache eCharts did not continue beyond 200 grid points. At 16,000 grid points, no library was able to complete the test.
| Fastest | Parameters | SciChart.js (FPS) | HighCharts (FPS) | Plotly.js (FPS) | eCharts (FPS) | LCJS v8 (FPS) |
|---|---|---|---|---|---|---|
| SciChart.js | 100 points, 1 series | 235.67 | 16.39 | 122.39 | 19.24 | 228.79 |
| SciChart.js | 200 points, 1 series | 235.76 | 4.85 | 72.34 | 5.18 | 231.02 |
| SciChart.js | 500 points, 1 series | 143.55 | 0.82 | 17.40 | 0.67 | 93.16 |
| SciChart.js | 1,000 points, 1 series | 39.97 | Skipped | 4.75 | Skipped | 27.75 |
| SciChart.js | 2,000 points, 1 series | 9.48 | Skipped | 1.16 | Skipped | 6.87 |
| LCJS v8 | 4,000 points, 1 series | 2.32 | Skipped | Hanging | Skipped | 2.94 |
| LCJS v8 | 8,000 points, 1 series | 0.48 | Skipped | Skipped | Skipped | 0.64 |
| - | 16,000 points, 1 series | Skipped | Skipped | Skipped | Skipped | Skipped |
Only SciChart.js, Plotly.js, Apache eCharts, and LCJS v8 support 3D point clouds; HighCharts, Chart.js, ChartGPU, and uPlot were excluded. SciChart.js scored the highest FPS in 4 out of 7 test configurations, Apache eCharts took 2 wins at the smallest sizes (100 and 1,000 points), and LCJS v8 took 1 win at 10,000 points.
At 4 million points, SciChart.js (3.51 FPS) was approximately 2x faster than LCJS v8 (1.76 FPS) — the only two libraries able to render at that scale. At 100,000 points, SciChart.js (121.34 FPS) was approximately 48% faster than LCJS v8 (81.76 FPS) and 14x faster than Apache eCharts (8.60 FPS). Plotly.js encountered an error at 1 million points and did not continue beyond that. Apache eCharts did not continue beyond 2 million points.
| Fastest | Parameters | SciChart.js (FPS) | Plotly.js (FPS) | eCharts (FPS) | LCJS v8 (FPS) |
|---|---|---|---|---|---|
| eCharts | 100 points, 1 series | 228.32 | 83.98 | 229.36 | 225.67 |
| eCharts | 1,000 points, 1 series | 230.18 | 51.24 | 239.69 | 236.47 |
| LCJS v8 | 10,000 points, 1 series | 229.52 | 12.59 | 82.37 | 236.65 |
| SciChart.js | 100,000 points, 1 series | 121.34 | 1.41 | 8.60 | 81.76 |
| SciChart.js | 1,000,000 points, 1 series | 15.09 | Error | 1.25 | 7.28 |
| SciChart.js | 2,000,000 points, 1 series | 7.52 | Skipped | 0.67 | 3.84 |
| SciChart.js | 4,000,000 points, 1 series | 3.51 | Skipped | Skipped | 1.76 |
Only SciChart.js, Plotly.js, Apache eCharts, and LCJS v8 support 3D surface meshes; HighCharts, Chart.js, ChartGPU, and uPlot were excluded. SciChart.js scored the highest FPS in 5 out of 6 completable test configurations, with LCJS v8 taking 1 win at 4,000 grid points. At 8,000 grid points, neither SciChart.js nor LCJS v8 completed the test.
At 500 grid size, SciChart.js (95.62 FPS) was approximately 44% faster than LCJS v8 (66.45 FPS). At 4,000 grid points, LCJS v8 (1.76 FPS) was approximately 38% faster than SciChart.js (1.28 FPS). Plotly.js did not complete the test at 2,000 grid points, and Apache eCharts did not continue beyond 1,000 grid points.
| Fastest | Parameters | SciChart.js (FPS) | Plotly.js (FPS) | eCharts (FPS) | LCJS v8 (FPS) |
|---|---|---|---|---|---|
| SciChart.js | 100 points, 1 series | 229.21 | 26.71 | 40.95 | 224.76 |
| SciChart.js | 200 points, 1 series | 231.35 | 24.41 | 15.02 | 193.44 |
| SciChart.js | 500 points, 1 series | 95.62 | 5.07 | 2.40 | 66.45 |
| SciChart.js | 1,000 points, 1 series | 27.69 | 1.34 | 0.48 | 19.06 |
| SciChart.js | 2,000 points, 1 series | 5.96 | Hanging | Skipped | 4.49 |
| LCJS v8 | 4,000 points, 1 series | 1.28 | Skipped | Skipped | 1.76 |
| - | 8,000 points, 1 series | Hanging | Skipped | Skipped | Hanging |
Based on FPS alone, across 102 test configurations spanning 13 test categories and 8 chart libraries:
- SciChart.js achieved the highest FPS score in 73 out of 102 test configurations (72%), with an average of 153.4 FPS, dominating 10 of 13 test categories including Line series which is unsorted in x, Column chart with data ascending in X, Brownian Motion Scatter Series and 7 more.
- LightningChart (LCJS v8) placed second overall with 15 wins out of 102 configurations (avg 127.9 FPS), and saw its best overall performance in the Series Compression Test.
- ChartGPU placed third overall with 6 wins out of 102 configurations (avg 94.0 FPS).
By running the test application, you can also view data such as data-ingestion rate, memory usage and initialisation time, plus, browse pre-recorded results on four hardware configurations (Intel, ARM, Apple and Raspberry Pi).
JavaScript charting performance is fundamentally architecture-bound. CPU-based and general-purpose charting libraries cannot scale to large, real-time, or multi-surface workloads. Only GPU-accelerated, purpose-built charting engines remain usable at extreme scale.
Open this folder in terminal and run the following commands:
npm installnpm start
Then visit https://localhost:5173 in your web browser.
We've included a number of pre-canned results from different hardware (Intel i9 / nVidia 4090, ARM Snapdragon) that you can view. Select these under the Result Sets section.
You can toggle between which metric to analyze on the homepage: FPS (Frames per second), Memory usage (MB), Initialisation Time (milliseconds), Total Frames, Data Ingestion rate (points per second)
You can run your own tests by clicking ">" in the table header on the homepage. Each test will open a new tab and run until completion.
It's recommended to only run one test at a time to ensure the CPU usage of each chart stress test does not interfere with another.
Close the tab after test completion. The homepage will automatically update with the test results.
The stress test page looks like this. Several tests will be run in sequence with ever more demanding requirements (more datapoints, more series, more charts). The test will automatically stop when completed.
Each test lasts 3 seconds. Each test case may have 5-10 total test runs. Leave the test to run until completion when the results table is shown.
Note: If any stress-test drops below 2 FPS, hangs, or is errored, subsequent tests will be skipped for that chart library / test case
The max data-point limit for realtime tests is capped at 100 Million datapoints to fit inside Google Chrome memory limits.
Some stats on the Stress Test page include:
- System information: Current browser, GPU, WebGL renderer, Platform, CPU cores, Video RAM
- Immediate stats:
- Current FPS: Showing the immediate "Frames per Second" (FPS) or refresh rate in Hertz
- Frame count: the number of frames that have rendered since the start of the test
- Memory: Immediate JavaScript memory (polled using
window.performance.memory?.usedJSHeapSize). This may not be available on all browsers - Datapoints: the total datapoint count currently displayed (will include all datapoints for multi-chart tests)
Once a test case has completed, the results will be persisted to IndexedDB and displayed in a table.
This results table includes:
- The library name and version, e.g. "SciChart.js 5.0.0"
- The test case type e.g. "Brownian Motion Scatter Series"
- The number of datapoints e.g. "10,000"
- The number of series on the chart
- The number of charts (or blank for single-chart cases)
- The time to library load in milliseconds
- The time to first frame in milliseconds
- The time to append data (data update rate)
- The JS memory used as reported by
window.performance.memory?.usedJSHeapSize - The Min, Max and Average (mean) FPS
- The total frames rendered in the test
- Any error conditions e.g. 'OK', 'HANGING', 'ERRORED'
If you close the tab and return to the homepage, the main tables will update with the test results (loaded from IndexedDB).
We've included a charts page which shows the FPS results in chart format. Click "Charts View" at the top of the page to switch to this.
EXPERIMENTAL
The automated test solution runs the app and opens each test case sequentially.
At the end of each test case it screenshots the page with results in the table and saves them as PDF.
After all test cases were executed the final "Results Summary" test in suite takes a PDF snapshot of the final results table at main app page.
Prerequisites
Playwright setup: npx playwright install
How to run
npm run test- default runs tests in headless modenpm run test:headed- runs tests in headed mode- Playwright UI Mode: run either of scripts with
-- --ui, e.g.npm run test:headed -- --ui
Playwright outputs PDFs with results and also a JSON file that can be imported via the UI to visualise results in tables/charts.
- Create a test type by updating G_TEST_GROUP_NAME in
public/before.js. For exampleCANDLESTICK_PERFORMANCE_TEST - Add test group record to
G_TEST_GROUPS. For example
const G_TEST_GROUPS = {
...
5: {
name: G_TEST_GROUP_NAME.CANDLESTICK_PERFORMANCE_TEST,
tests: [
{
series: 1,
points: 1000,
testDuration: 5000,
},
...
]
}
}
}- Create a test function for each test group in the suite. For example
eCandlestickPerformanceTestinscichart.js - Update select test if/else statement in
public/after.js. For example
if (testGroupName === G_TEST_GROUP_NAME.CANDLESTICK_PERFORMANCE_TEST) {
perfTest = eCandlestickPerformanceTest(seriesNumber, pointsNumber);
}- Update
generateCharts()function inpublic/main.jsfile to add new tests to the index page.
For any performance test, for example for eLinePerformanceTest, the order of calls is as follows:
createChartcreates a chartgenerateDatagenerates dataappendDataappends dataupdateChartupdates the chartdeleteChartdeletes the chart
















