How to Implement Real-Time Data Streaming Updates in Apache ECharts

Use chart.appendData() together with incremental rendering options (progressive and progressiveThreshold) to push new data points into existing series without triggering full chart re-layouts, enabling smooth high-frequency updates.

Apache ECharts provides a high-performance streaming architecture designed specifically for real-time data visualization scenarios. By leveraging the internal Scheduler pipeline and the appendData API, you can stream millions of data points into live charts without UI freezing or expensive full re-renders, as implemented in the apache/echarts repository.

Core Mechanisms for Streaming Data

ECharts supports real-time streaming through three coordinated systems that work together to minimize render overhead.

The appendData API

The primary entry point for streaming is chart.appendData(), defined in dist/echarts.js at ECharts.prototype.appendData (line 28568). This method pushes new raw data into an existing series without recreating the chart instance. Internally, it delegates to DefaultDataProvider.prototype.appendData (line 27122), which wraps the low-level appendDataSimply function to mutate the underlying data array directly.

Scheduler and Pipeline Management

The Scheduler class (starting at line 23737 in dist/echarts.js) orchestrates the data-processor, visual, and render stages. When new data arrives, the Scheduler marks only the affected tasks as dirty through methods like performDataProcessorTasks, performVisualTasks, and plan. This selective invalidation ensures that only incremental pipeline steps recompute, avoiding a full data flow restart.

Incremental (Progressive) Rendering

Incremental rendering activates when a series’ data length exceeds its progressiveThreshold. The Scheduler.prototype.updateStreamModes method (lines 23809–23827) detects this condition and enables progressiveRender: true for the series. Series views implementing incrementalPrepareRender can then draw only newly added items per animation frame, respecting the progressiveChunkMode (default "mod").

How the Streaming Pipeline Works

Understanding the internal execution flow helps optimize streaming performance:

  1. Initial Setup – When you call echarts.init and setOption, the Scheduler creates a data task for each series. If progressive is truthy and data exceeds progressiveThreshold, updateStreamModes records the series as eligible for incremental rendering.

  2. Data Appendingchart.appendData({ seriesIndex, data }) pushes records into the series’ raw data store via DefaultDataProvider.appendData.

  3. Task Invalidation – The Scheduler’s _performStageTasks walks the task graph and calls task.dirty() only for tasks associated with the mutated data source.

  4. Incremental Render – The plan method detects the next pipeline block. If view.incrementalPrepareRender exists, the view’s render method draws only the new points, skipping the full layout calculation.

  5. Frame Update – The chart updates in the current animation frame, producing the appearance of a smooth real-time stream.

appendData vs setOption: When to Use Each

Choosing the correct API depends on your update frequency and configuration needs:

  • chart.appendData – Use for high-frequency updates (tens to hundreds of points per second). Only new points are processed; no full option merge or re-layout occurs.
  • chart.setOption – Use when changing visual encodings (colors, symbols) or series configuration together with data. This recreates the series and runs the full data pipeline, incurring higher overhead.
  • setOption with progressive options – Use for initial loads of very large datasets. Progressive rendering chunks the initial draw to prevent UI freezing.

Implementation Examples

Basic Real-Time Streaming with JavaScript

Enable incremental rendering and push data at regular intervals:

// Initialize the chart
var chart = echarts.init(document.getElementById('main'));
chart.setOption({
  xAxis: { type: 'category' },
  yAxis: { type: 'value' },
  series: [{
    type: 'line',
    progressive: 500,          // Enable incremental after 500 points
    progressiveThreshold: 1000,
    data: []                   // Start empty
  }]
});

// Simulate live data source
function pushRandomPoint() {
  var now = new Date().toLocaleTimeString();
  var value = Math.round(Math.random() * 100);
  chart.appendData({
    seriesIndex: 0,
    data: [[now, value]]
  });
}

// Push every 200ms
setInterval(pushRandomPoint, 200);

Key source references: ECharts.prototype.appendData (line 28568), DefaultDataProvider (line 27122), Scheduler.prototype.updateStreamModes (line 23809).

WebSocket Real-Time Integration

Integrate with WebSocket or Server-Sent Events for server-pushed data:

var ws = new WebSocket('wss://example.com/stream');
ws.onmessage = function (event) {
  var payload = JSON.parse(event.data);
  // Synchronous append immediately schedules incremental render
  chart.appendData({
    seriesIndex: 0,
    data: [[payload.ts, payload.value]]
  });
};

Because appendData operates synchronously, the Scheduler queues the incremental render pass immediately upon message arrival, maintaining UI responsiveness without additional setOption calls.

Batch Updates for High-Frequency Streams

Reduce Scheduler overhead by batching multiple points into a single call:

var batch = [];
for (var i = 0; i < 100; i++) {
  var t = new Date(Date.now() + i * 1000).toLocaleTimeString();
  batch.push([t, Math.random() * 200]);
}
chart.appendData({
  seriesIndex: 0,
  data: batch   // Array of [category, value] pairs
});

Processing 100 points in one call triggers a single dirty-mark cycle, significantly outperforming 100 individual appendData invocations.

Switching Incremental Modes Dynamically

Disable incremental rendering when data volume drops below thresholds:

chart.setOption({
  series: [{ 
    progressive: 0,          // Disable incremental mode
    data: chart.getOption().series[0].data   // Preserve existing data
  }]
}, false);

Subsequent appendData calls will trigger full re-layouts, which is optimal when streaming stops and data volume becomes manageable for standard rendering.

Key Source Files and Functions

The following locations in the apache/echarts repository contain the core streaming implementation:

  • dist/echarts.jsECharts.prototype.appendData (≈ line 28568): Public API entry point.
  • dist/echarts.jsDefaultDataProvider.prototype.appendData & appendDataSimply (≈ lines 27122–27133): Low-level data store mutation.
  • dist/echarts.jsScheduler class (starts at line 23737): Core streaming pipeline with methods updateStreamModes, performDataProcessorTasks, plan, and updatePayload.
  • test/stream-basic.js: Minimal test case demonstrating live-update loops.

Direct GitHub references:

Summary

  • Use chart.appendData() for high-frequency real-time streaming to avoid full chart re-layouts.
  • Configure progressive and progressiveThreshold on series to enable incremental rendering when data volumes grow large.
  • The Scheduler class manages dirty task marking and executes only necessary pipeline stages via updateStreamModes and plan.
  • DefaultDataProvider handles efficient data mutation through appendDataSimply without array recreation.
  • Batch updates via appendData with arrays are significantly more efficient than single-point calls.
  • WebSocket and Server-Sent Events integrate seamlessly with the synchronous appendData API, triggering immediate incremental renders.

Frequently Asked Questions

What is the difference between appendData and setOption in ECharts?

appendData performs an incremental mutation of existing series data without recreating components or running full layout calculations, making it ideal for high-frequency streaming. setOption performs a complete option merge that recreates series and runs the entire data pipeline, which is necessary when changing visual encodings or structural configurations but too expensive for continuous streaming updates.

How does ECharts maintain performance with millions of streaming data points?

The Scheduler class implements incremental rendering controlled by progressiveThreshold. When the data length exceeds this threshold, the system activates progressiveRender mode, drawing only newly appended data blocks in subsequent animation frames rather than redrawing the entire dataset. This architecture, combined with the DefaultDataProvider efficient array appending, keeps memory and CPU usage bounded even with millions of points.

Can I use appendData with any chart type?

No, appendData works only with series types that expose a data source and implement incremental view methods such as incrementalPrepareRender. This includes line, bar, and scatter series. Series without incremental support require setOption for data updates, which triggers full re-renders.

Is the appendData operation synchronous or asynchronous?

appendData is synchronous. When called, it immediately mutates the underlying data provider via DefaultDataProvider.appendData, and the Scheduler synchronously marks the relevant pipeline tasks as dirty. The actual visual update occurs in the next animation frame, but the data is available in the chart instance immediately after the call returns.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →