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

> Implement real-time data streaming updates in Apache ECharts using appendData and incremental rendering. Achieve smooth, high-frequency chart updates without full re-layouts.

- Repository: [The Apache Software Foundation/echarts](https://github.com/apache/echarts)
- Tags: how-to-guide
- Published: 2026-03-04

---

**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`](https://github.com/apache/echarts/blob/main/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`](https://github.com/apache/echarts/blob/main/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 Appending** – `chart.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:

```javascript
// 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:

```javascript
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:

```javascript
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:

```javascript
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.js`](https://github.com/apache/echarts/blob/main/dist/echarts.js)** – `ECharts.prototype.appendData` (≈ line 28568): Public API entry point.
- **[`dist/echarts.js`](https://github.com/apache/echarts/blob/main/dist/echarts.js)** – `DefaultDataProvider.prototype.appendData` & `appendDataSimply` (≈ lines 27122–27133): Low-level data store mutation.
- **[`dist/echarts.js`](https://github.com/apache/echarts/blob/main/dist/echarts.js)** – `Scheduler` class (starts at line 23737): Core streaming pipeline with methods `updateStreamModes`, `performDataProcessorTasks`, `plan`, and `updatePayload`.
- **[`test/stream-basic.js`](https://github.com/apache/echarts/blob/main/test/stream-basic.js)**: Minimal test case demonstrating live-update loops.

Direct GitHub references:
- `ECharts.prototype.appendData`: https://github.com/apache/echarts/blob/master/dist/echarts.js#L28568
- `DefaultDataProvider.appendData`: https://github.com/apache/echarts/blob/master/dist/echarts.js#L27122
- `Scheduler` implementation: https://github.com/apache/echarts/blob/master/dist/echarts.js#L23737

## 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.