If you haven't read already, at SciChart we're developing a Javascript chart component which specialises in fast 2D/3D charts & graphs for your Javascript, Typescript, React, Angular and possibly even Blazor applications. SciChart already exists on other platforms, such as Windows (WPF), iOS and Android and boasts outstanding performance: able to draw millions of data-points in real-time on mobile, or even plot billions of data-points in real-time on Windows. This opens up a whole host of previously impossible applications, such as: visualising real-time ECG waveforms on a mobile device, big data analysis of telemetry on Windows PCs, or field lidar-visualisation on an iPad.

So what about Javascript? Can we match the incredible performance of SciChart on this platform? Turns out we can! We've taken our 3D engine - the result of years of Research and Development and compiled it to WebAssembly with WebGL. In a previous post we talked about our progress toward SciChart.js and showcased some real-time charts, and also showcased some Javascript 3D data-visualisation that we're building for web browsers.

In this post I'm going to show you a very quick demo of the speed & performance of SciChart for Javascript: loading 1 million data-points in under 15 milliseconds.

This is a simple test which shows how fast SciChart.js is at loading, processing and drawing data. Results below!

The Performance Test Source Code

SciChart.js is still in development, and we don't have a version released for testing just yet, but we wanted to share the source-code for the example so you can see how we're logging time.

First of all we have to create the SciChartSurface. At the moment in the API there is a helper function which initializes and returns a webassembly context. We might change this before the first release. Then there is code to add XAxis, YAxis and set RenderableSeries. SciChart has the concept of separating out RenderableSeries and DataSeries so you can update an object in real-time and change how it draws or renders separately.

// Create the WebAssembly context and SciChartSurface
const { wasmContext, sciChartSurface } = await createSciChartSurface(divElementId, WIDTH, HEIGHT);

// Apply a theme
sciChartSurface.applyTheme(new SciChartJSDarkTheme());

// Create an XAxis and add to the chart
const xAxis = new NumericAxis(wasmContext, {
    axisAlignment: EAxisAlignment.Top,
    visibleRange: new NumberRange(0, 1000000),
    autoRange: EAutoRange.Never
});
sciChartSurface.xAxes.add(xAxis);
// Create a YAxis and add to the chart
const yAxis = new NumericAxis(wasmContext, {
    axisAlignment: EAxisAlignment.Left,
    visibleRange: new NumberRange(-5000, 5000),
    autoRange: EAutoRange.Never
});
sciChartSurface.yAxes.add(yAxis);

// Create a DataSeries of Xy data
const dataSeries = new XyDataSeries(wasmContext);
// Create the Line series and set properties
const lineSeries = new FastLineRenderableSeries(wasmContext, { dataSeries, strokeThickness: 2 });
// Add the line series to the chart
sciChartSurface.renderableSeries.add(lineSeries);
lineSeries.stroke = "#42b649";

// Set interactivity modifiers: Zoom, pan, mousewheel zoom
sciChartSurface.chartModifiers.add(
    new ZoomExtentsModifier(), 
    new ZoomPanModifier(), 
    new MouseWheelZoomModifier());

This code creates a SciChartSurface, adds the axis and renders the empty chart on the screen. The output so far can be seen below:

The empty SciChartSurface. Now to create a click handler which loads some data.

Next we add a button click handler to load the data and perform the performance test. The click handler code looks like this:

// Buttons for chart
const loadPoints = () => {
    console.log("load points");

    // Clear state
    dataSeries.clear();
    const newTimeSpans: TTimeSpan[] = [];

    // Start counting Points generation time
    const generateTimestamp = Date.now();

    // Generate 1,000,000 points as a Random walk 
    const POINTS = 1000000;
    const xValues = new Array(POINTS);
    const yValues = new Array(POINTS);
    let prevYValue = 0;
    for (let i = 0; i < POINTS; i++) {
        const curYValue = Math.random() * 10 - 5;
        xValues[i] = i;
        yValues[i] = prevYValue + curYValue;
        prevYValue += curYValue;
    }

    // Log time: Generating 1M data points
    newTimeSpans.push({
        title: "Generate 1M Data Points",
        durationMs: Date.now() - generateTimestamp
    });

    // Start counting data append time
    const appendTimestamp = Date.now();
    dataSeries.appendRange(xValues, yValues);

    // Log time: Appending data to SciCHart
    newTimeSpans.push({
        title: "Append 1M Data Points",
        durationMs: Date.now() - appendTimestamp
    });

    // Subscribe to sciChartSurface.rendered event,
    // and calculate time duration for drawing
    const firstFrameTimestamp = Date.now();
    let frameIndex: number = 0;
    // We're going to measure 10 frames 
    const numberFramesToMeasure: number = 10;
    let nextFramesTimestamp: number;
    const handler = () => {
        if (frameIndex === 0) {            
            newTimeSpans.push({
                title: "Render the first frame",
                durationMs: Date.now() - firstFrameTimestamp
            });
            nextFramesTimestamp = Date.now();
            // sciChartSurface.zoomExtents();
        } else if (frameIndex === numberFramesToMeasure + 1) {            
            newTimeSpans.push({
                title: "Render next frames average",
                durationMs: (Date.now() - nextFramesTimestamp) / numberFramesToMeasure
            });

            // After 10 frames update the UI and unsubscribe from sciChartSurface.rendered
            updateTimeSpans(newTimeSpans);
            sciChartSurface.rendered.unsubscribe(handler);
        }
        // Trigger a redraw immediately 
        setTimeout(sciChartSurface.invalidateElement, 0);
        // Increment frame index
        frameIndex++;
    };
    sciChartSurface.rendered.subscribe(handler);
};

document.getElementById("loadPoints").addEventListener("click", loadPoints);

When we click the button the code above is going to create a 1-million point (x,y values) of a Random walk, and then set it into SciChart.js using dataSeries.appendRange(xValues, yValues). Finally we use the SciChartSurface.rendered event (callback) to measure actual time between draws and calculate the time to render the first frame, and subsequent 10 frames to compute an average.

Here are the results!

The World's Fastest JavaScript Chart? 1 Million Points in 15 milliseconds
In the performance test, SciChart.js renders 1,000,000 data-points in 35ms (first draw), and 15ms (subsequent draw). Data generation takes 27ms and appending to the chart takes 35ms.

Test 1 (first click after showing a chart)

  • Generate 1M points of random walk: 27ms
  • Append 1M points to Scichart: 34ms
  • Render the first frame: 35ms
  • Total data-append and draw time: 69ms

Test 2 (subsequent click)

  • Generate 1M points of random walk: 28ms
  • Append 1M points to Scichart: 37ms
  • Render the first frame: 15ms
  • Total data-append and draw time: 52ms

Fully Interactive Big Data Charts

What's more, this data is all there. We haven't down sampled it or pre-processed it at all. It's not a static image and it's not server-side rendered. You can actually zoom and pan right into the chart and see all the data. Take a look:

The World's Fastest JavaScript Chart? 1 Million Points in 15 milliseconds
SciChart.js allows you to browse big data-sets statically or in real-time and does not pre-process or downsample data.

Using Emscripten Compiler Options for further Performance Improvements

But wait! 50-70 milliseconds is pretty good but can it go any further? Turns out that it can. When I did the above test I forgot to enable Emscripten compiler options for maximum performance ๐Ÿ˜€.

There's a rundown on what the different compiler options do at the Emscripten compiler documentation. For the following test I enabled flag -O3 Maximum Speed optimisation. This also reduces wasm / WebAssembly file size also making it blazing fast for use on the web. Here is the result:

SciChart.js Performance Test: Loading 1 Million Points in under 15 milliseconds. This is the world's fastest Javascript chart!
SciChart.js Performance Test: Loading 1 Million Points in under 15 milliseconds. This is the world's fastest Javascript chart!

Wow!! ๐Ÿš€๐Ÿš€ That's just incredible. One million points appended to the chart, and rendered in 13 milliseconds when full optimisations are enabled. There is not a Javascript chart library in the world that can match this performance. The speed, power and performance of SciChat with WebAssembly/WebGL is just unmatched...

Incredible Performance in a Javascript Chart

SciChart.js in the test above is able to draw add and draw million data-points in line charts in under 15 milliseconds, with a redraw time as fast as 10ms once the data has been loaded. This incredible performance of SciChart.js opens up a whole host of applications that were previously impossible, now made possible.

For example:

  • Could you imagine a streaming dashboard of patient ECG data on a doctors computer or mobile device written in Javascript?
  • Creating a field scientific or surveying device that plots data from multiple sensors in real-time in Javascript?
  • Plotting telemetry from formula one vehicles or machinery in an automated process to devices and applications written in Javascript.

All the above were previously impossible projects, but will be made possible by SciChart.js - High Performance Realtime Javascript Charts.

SciChart.js webinar in a bit more detail: ECG, Realtime Stock Charts, 1 Million Points & more!

SciChart.js Release Date

I'm sure you're very keen to get your hands on our Webassembly / WebGL Charts and start working with them! We donโ€™t have a release date just yet, but we are targeting Q3/Q4 of this year for the first release. If youโ€™re interested to be notified when there are updates, when we have new demos or a BETA version, then subscribe to our SciChart.js mailing list by filling the form below!

About SciChart: High Performance Realtime Charts

SciChart provides high performance realtime chart components on the WPF (Windows), iOS, Android and Xamarin platforms. It is our goal to create the best cross-platform Native WPF, iOS, Android and JavaScript 2D & 3D Charts in the world, focusing on performance, developer productivity, ease of use, depth of features and enterprise-grade tech support.

If you have a question about what SciChart's WebGL / WebAssembly Charts can offer you, or if you would like to get a quote, please contact us. Our friendly, helpful support team will be glad to help!