SciChart.js JavaScript 2D Charts API > 2D Chart Types > DataSeries (Data Updates) API > DataSeries Realtime Updates
DataSeries Realtime Updates

As previously covered any modification to the DataSeries e.g. via calling append()insert()update()remove() or clear() will trigger a redraw on the chart.

Redraws are throttled so that a redraw only occurs every 1/60th of a second, no matter how often you update data.

Below we're going to talk about the four modes of DataSeries Realtime updates and how to achieve them in SciChart.js.

Appending Data

Appending data is a dynamic chart scenario where you start off with 0..N X,Y values then append a new batch of X,Y values via dataSeries.appendRange(). With the correct flags on the axis the chart will grow to fit all data. Memory grows until you stop appending or you reset the chart via calling dataSeries.clear().

Here's an example:

// Create a DataSeries
const xyDataSeries = new XyDataSeries(wasmContext, {
  // Optional: pass X,Y values to DataSeries constructor for fast initialization
  xValues,
  yValues
});

// Create a renderableSeries and assign the dataSeries
sciChartSurface.renderableSeries.add(new FastLineRenderableSeries(wasmContext, {
  dataSeries: xyDataSeries,
  strokeThickness: 3,
  stroke: "#50C7E0"
}));

// Now let's use a timeout to appendRange() 10 new values every 20ms. After N appends, reset the dataSeries
let updateCount = 0;
const updateCallback = () => {
  const xUpdate = [];
  const yUpdate = [];
  for(let j = 0; j < 10; i++, j++) {
    xUpdate.push(i);
    yUpdate.push(0.2 * Math.sin(i*0.1) - Math.cos(i * 0.01));
  }
  xyDataSeries.appendRange(xUpdate, yUpdate);

  // Just putting this in to reset the dataseries after N updates. We don't want the codepen example to grow infinitely!
  if (++updateCount % 250 === 0) {
    xyDataSeries.clear();
    j = 0;
    updateCount = 0;
  }
}

setTimeout(() => {
  updateCallback();
  setInterval(updateCallback, 20);
}, 20);

This results in the following output

<div id="scichart-root" ></div>
  
body { margin: 0; }
#scichart-root { width: 100%; height: 100vh; }
  
async function dataSeriesAppending(divElementId) {

  const {
    SciChartSurface,
    NumericAxis,
    FastLineRenderableSeries,
    XyDataSeries,
    SciChartJsNavyTheme,
    EAutoRange
  } = SciChart;

  // or for npm import { SciChartSurface, ... } from "scichart"

  // Create a chart surface
  const { sciChartSurface, wasmContext } = await SciChartSurface.create(divElementId, {
    theme: new SciChartJsNavyTheme(),
    title: "Appending Data example every 20ms",
    titleStyle: { fontSize: 16 }
  });

  // For the example to work, axis must have EAutoRange.Always
  sciChartSurface.xAxes.add(new NumericAxis(wasmContext, { autoRange: EAutoRange.Always, axisTitle: "X Axis autoranged" }));
  sciChartSurface.yAxes.add(new NumericAxis(wasmContext, { autoRange: EAutoRange.Always, axisTitle: "Y Axis autoranged" }));

  // Start off with N X,Y values in the series
  const xValues = [];
  const yValues = [];
  let i = 0;
  for(; i < 100; i++) {
    xValues.push(i);
    yValues.push(0.2 * Math.sin(i*0.1) - Math.cos(i * 0.01));
  }

  // #region ExampleA
  // Create a DataSeries
  const xyDataSeries = new XyDataSeries(wasmContext, {
    // Optional: pass X,Y values to DataSeries constructor for fast initialization
    xValues,
    yValues
  });

  // Create a renderableSeries and assign the dataSeries
  sciChartSurface.renderableSeries.add(new FastLineRenderableSeries(wasmContext, {
    dataSeries: xyDataSeries,
    strokeThickness: 3,
    stroke: "#50C7E0"
  }));

  // Now let's use a timeout to appendRange() 10 new values every 20ms. After N appends, reset the dataSeries
  let updateCount = 0;
  const updateCallback = () => {
    const xUpdate = [];
    const yUpdate = [];
    for(let j = 0; j < 10; i++, j++) {
      xUpdate.push(i);
      yUpdate.push(0.2 * Math.sin(i*0.1) - Math.cos(i * 0.01));
    }
    xyDataSeries.appendRange(xUpdate, yUpdate);

    // Just putting this in to reset the dataseries after N updates. We don't want the codepen example to grow infinitely!
    if (++updateCount % 250 === 0) {
      xyDataSeries.clear();
      j = 0;
      updateCount = 0;
    }
  }

  setTimeout(() => {
    updateCallback();
    setInterval(updateCallback, 20);
  }, 20);
  // #endregion
}

dataSeriesAppending("scichart-root");




  

Replacing Data

Replacing data is a real-time scenario which would allow you to make a spectral-analyzer type chart, where all data is replaced every time the chart is updated.

In SciChart.js, we achieve this by using dataSeries.clear() followed by dataSeries.appendRange().

// Create a DataSeries
const xyDataSeries = new XyDataSeries(wasmContext, {
  // Optional: pass X,Y values to DataSeries constructor for fast initialization
  xValues: [],
  yValues: []
});

// Create a renderableSeries and assign the dataSeries
sciChartSurface.renderableSeries.add(new FastLineRenderableSeries(wasmContext, {
  dataSeries: xyDataSeries,
  strokeThickness: 3,
  stroke: "#50C7E0"
}));

// Now let's use a timeout to clear() and appendRange() entirely new values every 20ms.
const updateCallback = () => {
  const xValues = [];
  const yValues = [];
  for(let i = 0; i < 100; i++) {
    xValues.push(i);
    yValues.push(Math.random() * Math.sin(i*0.1) - Math.cos(i * 0.01));
  }
  xyDataSeries.clear();
  xyDataSeries.appendRange(xValues, yValues);
}

setTimeout(() => {
  updateCallback();
  setInterval(updateCallback, 20);
}, 20);

This results in the following output:

<div id="scichart-root" ></div>
  
body { margin: 0; }
#scichart-root { width: 100%; height: 100vh; }
  
async function dataSeriesReplacing(divElementId) {

  const {
    SciChartSurface,
    NumericAxis,
    FastLineRenderableSeries,
    XyDataSeries,
    SciChartJsNavyTheme,
    EAutoRange,
    NumberRange
  } = SciChart;

  // or for npm import { SciChartSurface, ... } from "scichart"

  // Create a chart surface
  const { sciChartSurface, wasmContext } = await SciChartSurface.create(divElementId, {
    theme: new SciChartJsNavyTheme(),
    title: "Replacing all Data example every 20ms",
    titleStyle: { fontSize: 16 }
  });

  // For the example to work, axis must have EAutoRange.Always
  sciChartSurface.xAxes.add(new NumericAxis(wasmContext, { autoRange: EAutoRange.Always, axisTitle: "X Axis autoranged" }));
  sciChartSurface.yAxes.add(new NumericAxis(wasmContext, { visibleRange: new NumberRange(-2, 0.5), axisTitle: "Y Axis fixed range [-1, 1]" }));

  // #region ExampleA
  // Create a DataSeries
  const xyDataSeries = new XyDataSeries(wasmContext, {
    // Optional: pass X,Y values to DataSeries constructor for fast initialization
    xValues: [],
    yValues: []
  });

  // Create a renderableSeries and assign the dataSeries
  sciChartSurface.renderableSeries.add(new FastLineRenderableSeries(wasmContext, {
    dataSeries: xyDataSeries,
    strokeThickness: 3,
    stroke: "#50C7E0"
  }));

  // Now let's use a timeout to clear() and appendRange() entirely new values every 20ms.
  const updateCallback = () => {
    const xValues = [];
    const yValues = [];
    for(let i = 0; i < 100; i++) {
      xValues.push(i);
      yValues.push(Math.random() * Math.sin(i*0.1) - Math.cos(i * 0.01));
    }
    xyDataSeries.clear();
    xyDataSeries.appendRange(xValues, yValues);
  }

  setTimeout(() => {
    updateCallback();
    setInterval(updateCallback, 20);
  }, 20);
  // #endregion
}

dataSeriesReplacing("scichart-root");




  

Scrolling Data

Scrolling data can be achieved by appending then removing data so that a fixed number of points remains in the dataSeries. This can be achieved via dataSeries.removeRange() then dataSeries.appendRange() but also you can use the new fifoCapacity flag available in SciChart.js v3.2.

Below we have an example of each:

Scrolling using appendRange() removeRange()

Here's an example of how to use dataSeries.removeRange() then dataSeries.appendRange()  to scroll a chart.

// Create a DataSeries
const xyDataSeries = new XyDataSeries(wasmContext, {
  // Optional: pass X,Y values to DataSeries constructor for fast initialization
  xValues,
  yValues
});

// Create a renderableSeries and assign the dataSeries
sciChartSurface.renderableSeries.add(new FastLineRenderableSeries(wasmContext, {
  dataSeries: xyDataSeries,
  strokeThickness: 3,
  stroke: "#50C7E0"
}));

// Now let's use a timeout to appendRange() 10 new values every 20ms.
// using removeRange() causes the number of points in the series to remain fixed and the chart to scroll
const updateCallback = () => {
  const xUpdate = [];
  const yUpdate = [];
  for(let j = 0; j < 5; i++, j++) {
    xUpdate.push(i);
    yUpdate.push(0.2 * Math.sin(i*0.1) - Math.cos(i * 0.01));
  }
  // Remove the first N points from the series
  xyDataSeries.removeRange(0, xUpdate.length);
  // Now append new points
  xyDataSeries.appendRange(xUpdate, yUpdate);
  // result: dataSeries length remains the same. as x-value increases, and xAxis.autoRange zooms to fit, the chart scrolls
}

setTimeout(() => {
  updateCallback();
  setInterval(updateCallback, 20);
}, 20);
<div id="scichart-root" ></div>
  
body { margin: 0; }
#scichart-root { width: 100%; height: 100vh; }
  
async function dataSeriesScrollingManually(divElementId) {

  const {
    SciChartSurface,
    NumericAxis,
    FastLineRenderableSeries,
    XyDataSeries,
    SciChartJsNavyTheme,
    EAutoRange
  } = SciChart;

  // or for npm import { SciChartSurface, ... } from "scichart"

  // Create a chart surface
  const { sciChartSurface, wasmContext } = await SciChartSurface.create(divElementId, {
    theme: new SciChartJsNavyTheme(),
    title: "Scrolling Data using removeRange() appendRange()",
    titleStyle: { fontSize: 16 }
  });

  // For the example to work, axis must have EAutoRange.Always
  sciChartSurface.xAxes.add(new NumericAxis(wasmContext, { autoRange: EAutoRange.Always, axisTitle: "X Axis autoranged" }));
  sciChartSurface.yAxes.add(new NumericAxis(wasmContext, { autoRange: EAutoRange.Always, axisTitle: "Y Axis autoranged" }));

  // Start off with N X,Y values in the series
  const xValues = [];
  const yValues = [];
  let i = 0;
  for(; i < 1000; i++) {
    xValues.push(i);
    yValues.push(0.2 * Math.sin(i*0.1) - Math.cos(i * 0.01));
  }

  // #region ExampleA
  // Create a DataSeries
  const xyDataSeries = new XyDataSeries(wasmContext, {
    // Optional: pass X,Y values to DataSeries constructor for fast initialization
    xValues,
    yValues
  });

  // Create a renderableSeries and assign the dataSeries
  sciChartSurface.renderableSeries.add(new FastLineRenderableSeries(wasmContext, {
    dataSeries: xyDataSeries,
    strokeThickness: 3,
    stroke: "#50C7E0"
  }));

  // Now let's use a timeout to appendRange() 10 new values every 20ms.
  // using removeRange() causes the number of points in the series to remain fixed and the chart to scroll
  const updateCallback = () => {
    const xUpdate = [];
    const yUpdate = [];
    for(let j = 0; j < 5; i++, j++) {
      xUpdate.push(i);
      yUpdate.push(0.2 * Math.sin(i*0.1) - Math.cos(i * 0.01));
    }
    // Remove the first N points from the series
    xyDataSeries.removeRange(0, xUpdate.length);
    // Now append new points
    xyDataSeries.appendRange(xUpdate, yUpdate);
    // result: dataSeries length remains the same. as x-value increases, and xAxis.autoRange zooms to fit, the chart scrolls
  }

  setTimeout(() => {
    updateCallback();
    setInterval(updateCallback, 20);
  }, 20);
  // #endregion
}

dataSeriesScrollingManually("scichart-root");




  

Scrolling using fifoCapacity

Since SciChart.js v3.2, we've introduced a much more efficient way to auto-discard old points. By setting dataSeries.fifoCapacity = N, when the capacity is exceeded, old points are discarded. FIFO series are a special case and are internally handled as a circular buffer. They cannot be resized.

// Create a DataSeries
const xyDataSeries = new XyDataSeries(wasmContext, {
  xValues,
  yValues,
  fifoCapacity: 1200 // set fifoCapacity to 1200. Requires scichart.js v3.2 or later
});

console.log(`version is ${libraryVersion}`);
console.log(`dataSeries.fifoCapacity is ${xyDataSeries.fifoCapacity}`);

// Create a renderableSeries and assign the dataSeries
sciChartSurface.renderableSeries.add(new FastLineRenderableSeries(wasmContext, {
  dataSeries: xyDataSeries,
  strokeThickness: 3,
  stroke: "#50C7E0"
}));

// Now let's use a timeout to appendRange() new values every 20ms.
// using removeRange() causes the number of points in the series to remain fixed and the chart to scroll
const updateCallback = () => {
  const xUpdate = [];
  const yUpdate = [];
  for(let j = 0; j < 5; i++, j++) {
    xUpdate.push(i);
    yUpdate.push(0.2 * Math.sin(i*0.1) - Math.cos(i * 0.01));
  }
  // With fifoCapacity set, just append new points.
  xyDataSeries.appendRange(xUpdate, yUpdate);
  // result: dataSeries length remains the same. point counts > fifoCapacity are discarded.
  // as x-value increases, and xAxis.autoRange zooms to fit, the chart scrolls
}

setTimeout(() => {
  updateCallback();
  setInterval(updateCallback, 20);
}, 20);
<div id="scichart-root" ></div>
  
body { margin: 0; }
#scichart-root { width: 100%; height: 100vh; }
  
async function dataSeriesScrollingFifo(divElementId) {

  const {
    SciChartSurface,
    NumericAxis,
    FastLineRenderableSeries,
    XyDataSeries,
    SciChartJsNavyTheme,
    EAutoRange,
    libraryVersion
  } = SciChart;

  // or for npm import { SciChartSurface, ... } from "scichart"

  // Create a chart surface
  const { sciChartSurface, wasmContext } = await SciChartSurface.create(divElementId, {
    theme: new SciChartJsNavyTheme(),
    title: "Scrolling Data using fifoCapacity",
    titleStyle: { fontSize: 16 }
  });

  // For the example to work, axis must have EAutoRange.Always
  sciChartSurface.xAxes.add(new NumericAxis(wasmContext, { autoRange: EAutoRange.Always, axisTitle: "X Axis autoranged" }));
  sciChartSurface.yAxes.add(new NumericAxis(wasmContext, { autoRange: EAutoRange.Always, axisTitle: "Y Axis autoranged" }));

  // Start off with N X,Y values in the series
  const xValues = [];
  const yValues = [];
  let i = 0;
  for(; i < 1000; i++) {
    xValues.push(i);
    yValues.push(0.2 * Math.sin(i*0.1) - Math.cos(i * 0.01));
  }

  // #region ExampleA
  // Create a DataSeries
  const xyDataSeries = new XyDataSeries(wasmContext, {
    xValues,
    yValues,
    fifoCapacity: 1200 // set fifoCapacity to 1200. Requires scichart.js v3.2 or later
  });

  console.log(`version is ${libraryVersion}`);
  console.log(`dataSeries.fifoCapacity is ${xyDataSeries.fifoCapacity}`);

  // Create a renderableSeries and assign the dataSeries
  sciChartSurface.renderableSeries.add(new FastLineRenderableSeries(wasmContext, {
    dataSeries: xyDataSeries,
    strokeThickness: 3,
    stroke: "#50C7E0"
  }));

  // Now let's use a timeout to appendRange() new values every 20ms.
  // using removeRange() causes the number of points in the series to remain fixed and the chart to scroll
  const updateCallback = () => {
    const xUpdate = [];
    const yUpdate = [];
    for(let j = 0; j < 5; i++, j++) {
      xUpdate.push(i);
      yUpdate.push(0.2 * Math.sin(i*0.1) - Math.cos(i * 0.01));
    }
    // With fifoCapacity set, just append new points.
    xyDataSeries.appendRange(xUpdate, yUpdate);
    // result: dataSeries length remains the same. point counts > fifoCapacity are discarded.
    // as x-value increases, and xAxis.autoRange zooms to fit, the chart scrolls
  }

  setTimeout(() => {
    updateCallback();
    setInterval(updateCallback, 20);
  }, 20);
  // #endregion
}

dataSeriesScrollingFifo("scichart-root");




  

Sweeping Data

Another mode that we've added in SciChart.js v3.2, and the last real-time update mode is Fifo Sweeping.

With dataSeries.fifoCapacity set, also setting dataSeries.fifoSweeping = true, setting an optional dataSeries.fifoSweepingGap and having the correct type of xAxis or modulation of x-data, you can achieve allowing the chart to wrap-around once the trace reaches the right edge of the viewport.

// Create a DataSeries
const xyDataSeries = new XyDataSeries(wasmContext, {
  xValues,
  yValues,
  fifoCapacity: 1000, // set fifoCapacity. Requires scichart.js v3.2 or later
  fifoSweeping: true,
  fifoSweepingGap: 20
});

console.log(`version is ${libraryVersion}`);
console.log(`dataSeries.fifoCapacity is ${xyDataSeries.fifoCapacity}`);

// Create a renderableSeries and assign the dataSeries
sciChartSurface.renderableSeries.add(new FastLineRenderableSeries(wasmContext, {
  dataSeries: xyDataSeries,
  pointMarker: new EllipsePointMarker(wasmContext, { width: 11, height: 11, fill: "#fff", lastPointOnly: true }),
  strokeThickness: 3,
  stroke: "#50C7E0"
}));

// Now let's use a timeout to appendRange() new values every 20ms.
// using removeRange() causes the number of points in the series to remain fixed and the chart to scroll
const updateCallback = () => {
  const xUpdate = [];
  const yUpdate = [];
  for(let j = 0; j < 5; i++, j++) {
    xUpdate.push(i % fifoCapacity);
    yUpdate.push(0.2 * Math.sin(i*0.1) - Math.cos(i * 0.01));
  }
  // With fifoCapacity set, just append new points.
  xyDataSeries.appendRange(xUpdate, yUpdate);
  // result: dataSeries length remains the same. point counts > fifoCapacity are discarded.
  // as x-value increases, and xAxis.autoRange zooms to fit, the chart scrolls
}

setTimeout(() => {
  updateCallback();
  setInterval(updateCallback, 20);
}, 20);
<div id="scichart-root" ></div>
  
body { margin: 0; }
#scichart-root { width: 100%; height: 100vh; }
  
async function dataSeriesScrollingFifo(divElementId) {

  const {
    SciChartSurface,
    NumericAxis,
    FastLineRenderableSeries,
    XyDataSeries,
    SciChartJsNavyTheme,
    EAutoRange,
    CategoryAxis,
    libraryVersion,
    EllipsePointMarker
  } = SciChart;

  // or for npm import { SciChartSurface, ... } from "scichart"

  // Create a chart surface
  const { sciChartSurface, wasmContext } = await SciChartSurface.create(divElementId, {
    theme: new SciChartJsNavyTheme(),
    title: "Sweeping Data using fifoSweeping",
    titleStyle: { fontSize: 16 }
  });

  // For the example to work, axis must have EAutoRange.Always
  sciChartSurface.xAxes.add(new NumericAxis(wasmContext, { autoRange: EAutoRange.Always, axisTitle: "X Axis autoranged" }));
  sciChartSurface.yAxes.add(new NumericAxis(wasmContext, { autoRange: EAutoRange.Always, axisTitle: "Y Axis autoranged" }));

  // Start off with N X,Y values in the series
  const xValues = [];
  const yValues = [];
  let i = 0;
  const fifoCapacity = 1000;
  for(; i < fifoCapacity; i++) {
    xValues.push(i % fifoCapacity);
    yValues.push(0.2 * Math.sin(i*0.1) - Math.cos(i * 0.01));
  }

  // #region ExampleA
  // Create a DataSeries
  const xyDataSeries = new XyDataSeries(wasmContext, {
    xValues,
    yValues,
    fifoCapacity: 1000, // set fifoCapacity. Requires scichart.js v3.2 or later
    fifoSweeping: true,
    fifoSweepingGap: 20
  });

  console.log(`version is ${libraryVersion}`);
  console.log(`dataSeries.fifoCapacity is ${xyDataSeries.fifoCapacity}`);

  // Create a renderableSeries and assign the dataSeries
  sciChartSurface.renderableSeries.add(new FastLineRenderableSeries(wasmContext, {
    dataSeries: xyDataSeries,
    pointMarker: new EllipsePointMarker(wasmContext, { width: 11, height: 11, fill: "#fff", lastPointOnly: true }),
    strokeThickness: 3,
    stroke: "#50C7E0"
  }));

  // Now let's use a timeout to appendRange() new values every 20ms.
  // using removeRange() causes the number of points in the series to remain fixed and the chart to scroll
  const updateCallback = () => {
    const xUpdate = [];
    const yUpdate = [];
    for(let j = 0; j < 5; i++, j++) {
      xUpdate.push(i % fifoCapacity);
      yUpdate.push(0.2 * Math.sin(i*0.1) - Math.cos(i * 0.01));
    }
    // With fifoCapacity set, just append new points.
    xyDataSeries.appendRange(xUpdate, yUpdate);
    // result: dataSeries length remains the same. point counts > fifoCapacity are discarded.
    // as x-value increases, and xAxis.autoRange zooms to fit, the chart scrolls
  }

  setTimeout(() => {
    updateCallback();
    setInterval(updateCallback, 20);
  }, 20);
  // #endregion
}

dataSeriesScrollingFifo("scichart-root");




  

Note: Sweeping requires a few special conditions. fifoCapacity must be set and fifoSweeping = true. Next, you must either use a CategoryAxis on the xAxis, or, modulate your data.

You can use NumericAxis but you must modulate your data. X must range from 0...fifoCapacity. In the example above we set xValue[i] = i % fifoCapacity

See a worked example at the ECG/Vital Signs monitor demo.