How to Optimize ECharts Performance for Large Datasets: A Complete Guide to Large-Mode Rendering
Enable ECharts large-mode by setting large: true or adjusting largeThreshold to switch from individual graphic elements to typed-array rendering, reducing memory usage and draw calls for datasets exceeding 2,000 points.
When visualizing massive datasets in the apache/echarts repository, the default rendering path creates individual graphic elements for every data point, causing memory pressure and frame drops. To optimize ECharts performance, the library implements an internal large-mode pipeline that uses typed arrays and batched draw calls instead of DOM-heavy graphic primitives.
Understanding ECharts Large-Mode Architecture
The large-mode mechanism relies on three core components working together during the rendering lifecycle.
The Scheduler and Pipeline Context
The Scheduler class in src/core/Scheduler.ts (lines 60‑63) maintains a PipelineContext that tracks whether the current frame should use large-mode rendering. This context holds two critical boolean flags:
large: Indicates whether the series should use typed-array rendering.progressiveRender: Controls whether data should be painted in chunks to maintain UI responsiveness.
The scheduler evaluates data length against series-specific thresholds to set these flags before the render cycle begins.
Render Planner and Series Models
The createRenderPlanner function in src/chart/helper/createRenderPlanner.ts (lines 43‑48) acts as a stage handler that reads the pipelineContext.large flag and applies it to the series model. When the flag changes between render cycles—such as when a dataset grows from 1,000 to 50,000 points—the planner returns 'reset', triggering a complete rebuild of the series layout and switching the rendering path from standard to large-mode.
Series models expose the configuration interface for this behavior:
ScatterSeries(src/chart/scatter/ScatterSeries.ts, lines 137‑140): Defaults tolarge: falsewithlargeThreshold: 2000.BarSeries(src/chart/bar/BarSeries.ts, lines 14‑20, 24‑31): ImplementsgetProgressive()andgetProgressiveThreshold()methods that adapt progressive settings when large mode activates.BaseBarSeries(src/chart/bar/BaseBarSeries.ts, line 204): Sets the defaultlargeflag tofalse.LinesSeries(src/chart/lines/LinesSeries.ts, line 410): Provides similar large-mode defaults for line-type visualizations.
How Large-Mode Rendering Works Internally
The transition from standard to large-mode involves five distinct stages that optimize memory layout and rendering throughput.
Threshold Detection and Pipeline Flags
During the update cycle, the scheduler compares the current data length against the series largeThreshold. For scatter series, the default threshold is 2000 points; for bar series, it is 400. The detection logic in src/core/Scheduler.ts (around line 230) evaluates:
const large = this.option.large ? true
: dataLen >= this.get('largeThreshold');
When this condition evaluates to true, the pipelineContext.large flag is set, signaling downstream components to use the optimized rendering path.
Typed-Array Layout and Memory Optimization
Once large-mode activates, the series stops creating individual ZRender graphic elements (which consume significant memory for DOM references and styling properties). Instead, it allocates compact typed arrays:
largePoints: AFloat32Arraystoring x/y coordinates.largeBackgroundPoints: Optional buffer for background geometries.largeDataIndices: An index mapping for interaction and tooltip lookups.
These buffers are processed by the renderer in a single draw call, bypassing the expensive per-element creation and update cycles of the standard path. This reduces memory usage from megabytes to kilobytes per thousand points and eliminates thousands of individual draw calls.
Progressive Rendering for Massive Data
For datasets exceeding 100,000 points, even typed-array rendering can cause frame drops during the initial paint. The progressive option enables chunked rendering:
// From BarSeries.ts lines 14-20
getProgressive(): number | false {
return this.get('large') ? (this.get('progressive') || 5000) : false;
}
The series returns a progressive step size (default 5000 for large mode) and a threshold (getProgressiveThreshold()) that is automatically enlarged to at least the largeThreshold. The scheduler uses these values to paint data in batches across multiple animation frames, keeping the UI responsive while the chart initializes.
Reset on Mode Change
If a dataset dynamically grows or shrinks across the threshold boundary, the render planner detects the flag change and returns 'reset' (createRenderPlanner.ts lines 46‑48). This triggers a complete series layout rebuild, ensuring the correct rendering path is used without visual artifacts or memory leaks from mixed-mode rendering.
Practical Configuration: Optimize ECharts Performance with Code Examples
Enabling Large-Mode for Scatter Plots
The scatter series provides the most dramatic performance improvements with large-mode due to the high point count typical in scatter visualizations.
const chart = echarts.init(document.getElementById('chart'));
// Generate 50,000 random data points
const data = Array.from({length: 50000}, () => [
Math.random() * 100,
Math.random() * 100
]);
const option = {
xAxis: {},
yAxis: {},
series: [{
type: 'scatter',
// Force large-mode regardless of data size
large: true,
// Only switch to large-mode when exceeding 10,000 points
largeThreshold: 10000,
// Render in batches of 5,000 points per frame
progressive: 5000,
data: data
}]
};
chart.setOption(option);
Key configuration points:
large: trueforces typed-array rendering even for smaller datasets, useful when you anticipate dynamic growth.largeThreshold: 10000overrides the default 2000-point threshold for scatter series as defined insrc/chart/scatter/ScatterSeries.ts.progressive: 5000ensures the UI remains responsive during the initial render of 50,000 points.
Configuring Progressive Rendering for Bar Charts
Bar charts use a lower default threshold (400 bars) for large-mode activation and adapt progressive settings automatically.
const option = {
xAxis: {
type: 'category',
data: Array.from({length: 20000}, (_, i) => `Item ${i}`)
},
yAxis: {},
series: [{
type: 'bar',
// Auto-detect large mode (default largeThreshold = 400)
// Set higher threshold so large-mode triggers only above 5,000 bars
largeThreshold: 5000,
// Chunk size for progressive painting
progressive: 2000,
data: Array.from({length: 20000}, () => Math.round(Math.random() * 1000))
}]
};
echarts.init(document.getElementById('chart')).setOption(option);
Implementation details from the source:
According to src/chart/bar/BarSeries.ts (lines 14‑20), the getProgressive() method returns this.get('large') ? (this.get('progressive') || 5000) : false. This means when large mode activates (either manually or via largeThreshold), the series automatically adopts progressive rendering with a default chunk size of 5000 unless overridden.
Dynamic Mode Switching After Data Updates
When datasets grow dynamically across the threshold boundary, ECharts automatically rebuilds the rendering pipeline.
// Initial small dataset within normal rendering limits
let data = Array.from({length: 1500}, () => [
Math.random() * 100,
Math.random() * 100
]);
chart.setOption({
series: [{
type: 'scatter',
data,
largeThreshold: 2000
}]
});
// Simulate data growth beyond threshold
setTimeout(() => {
data = Array.from({length: 80000}, () => [
Math.random() * 100,
Math.random() * 100
]);
chart.setOption({
series: [{
type: 'scatter',
data
// large will be auto-enabled (dataLen > largeThreshold)
}]
});
}, 3000);
Automatic pipeline reset:
As implemented in src/chart/helper/createRenderPlanner.ts (lines 46‑48), when the pipelineContext.large flag changes between true and false, the planner returns 'reset'. This triggers a complete series layout rebuild, ensuring the series switches from standard graphic element rendering to the typed-array largePoints buffer without visual artifacts.
Performance Tuning Best Practices
To maximize rendering speed when working with massive datasets in ECharts, apply these configuration strategies derived from the source code implementation:
-
Force large-mode explicitly with
large: truewhen you know datasets will exceed 10,000 points. This bypasses threshold detection and immediately allocatesFloat32Arraybuffers insrc/core/Scheduler.ts, preventing the memory spike of initial standard rendering. -
Raise the switching threshold using
largeThresholdwhen you need interactive features like individual point emphasis or labels for moderately large datasets (5,000–10,000 points). The default 2000 for scatter (src/chart/scatter/ScatterSeries.ts) may be conservative for modern hardware. -
Combine large-mode with progressive rendering by setting both
large: trueandprogressive: 5000. As defined insrc/chart/bar/BarSeries.ts, the series automatically enlarges progressive thresholds when large mode is active, painting data in chunks to maintain 60fps during initialization. -
Disable expensive visual effects including
animation,label, andemphasis.scalewhen using large-mode. While the typed-array path insrc/core/Scheduler.tsreduces memory overhead, per-point effects still require individual processing that defeats the optimization purpose. -
Monitor pipeline resets when dynamically updating data across threshold boundaries. The
createRenderPlanner.tslogic triggers a full series reset when switching modes, which may cause a brief frame drop; batch updates to stay consistently above or belowlargeThreshold.
Summary
Optimizing ECharts performance for large datasets requires understanding the library's internal large-mode pipeline that switches from individual graphic elements to typed-array rendering. Key takeaways include:
- Threshold-based activation: Series automatically switch to large-mode when data exceeds
largeThreshold(default 2000 for scatter, 400 for bar), controlled by theSchedulerinsrc/core/Scheduler.ts. - Memory efficiency: Large-mode stores coordinates in
Float32Arraybuffers (largePoints,largeDataIndices) rather than full ZRender elements, reducing memory usage by orders of magnitude. - Progressive rendering: The
progressiveoption chunks rendering across frames when combined with large-mode, preventing UI freezing during initial paint of 100,000+ points. - Dynamic adaptation: When datasets grow across threshold boundaries,
createRenderPlanner.tsautomatically triggers a series reset to rebuild the typed-array layout without manual intervention.
Frequently Asked Questions
What is the default threshold for activating large-mode in ECharts scatter plots?
The default largeThreshold for scatter series is 2000 points, as defined in src/chart/scatter/ScatterSeries.ts (lines 137‑140). When your dataset exceeds this count and large is not explicitly set to false, ECharts automatically switches to typed-array rendering to maintain performance.
How does large-mode reduce memory usage compared to standard rendering?
Standard rendering creates individual ZRender graphic elements for every data point, each carrying DOM references, style properties, and event handlers. In large-mode, ECharts stores data in compact typed arrays (Float32Array for coordinates, Uint32Array for indices) as implemented in src/core/Scheduler.ts. This reduces per-point memory overhead from hundreds of bytes to approximately 8 bytes per coordinate pair.
Can I use progressive rendering without enabling large-mode?
Yes, but with limitations. The progressive option works independently, but as implemented in src/chart/bar/BarSeries.ts (lines 14‑20), the getProgressive() method returns false when large is disabled unless you explicitly set a progressive value in your options. For optimal performance with datasets exceeding 50,000 points, combining both features is recommended.
Why does my chart flicker or reset when updating data across the large-mode threshold?
This occurs because src/chart/helper/createRenderPlanner.ts (lines 46‑48) detects when the pipelineContext.large flag changes between true and false. To prevent mixed-mode rendering artifacts, the planner returns 'reset', forcing the series to rebuild its internal layout and switch from standard graphic elements to typed-array buffers (or vice versa). To minimize disruption, keep largeThreshold consistent or explicitly set large: true to avoid automatic threshold crossing.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →