SciChart.js Performance Demo: 1 Million Datapoints in under 15ms
Tags: webdev, javascript, datapoints
Published: 2024-04-05 11:40:28

<!-- wp:html -->
<div style="background-color: #f3f3f3; border: 1px solid #E3E3E3; padding: 20px 20px 0px 20px; margin-bottom: 20px;">
<h2><span style="color: #078e26;">//</span> Are you looking for the Fastest JavaScript Charts?</h2>
<div class="column one-second column_column">
<p>SciChart.js, our <a href="https://www.scichart.com/javascript-chart-features/">Ultra-High Performance JavaScript Chart library</a> is now released!</p>
<p><em>“With WebAssembly and WebGL hardware acceleration and an optimized render pipeline, <strong>SciChart provides extremely fast JavaScript charts capable of drawing millions of data-points in realtime …</strong>” <a href="https://www.scichart.com/javascript-chart-features/" target="_blank" rel="noopener noreferrer">[read more]</a></em></p>
<p><a class="button" style="margin-bottom: 0px;" tabindex="0" href="https://www.scichart.com/javascript-chart-features/" target="_blank" rel="noopener noreferrer">WHY SCICHART: THE FASTEST JAVASCRIPT CHARTS</a></p>
</div>
<div class="column one-second column_column">
<div class="aos-init aos-animate" style="margin-top: 0px !important;" data-aos="fade-left" data-aos-duration="1000" data-aos-delay="500">
<figure class="wp-block-video"><video id="html5_video_65r0s6fr8m5" style="max-width: 100%;" src="https://abtsoftware-wpengine.netdna-ssl.com/wp-content/uploads/2020/10/scichart-js-meta-og-video.mp4" autoplay="autoplay" loop="loop" muted="" controls="controls" width="300" height="150"></video>
<figcaption><strong>See our live demos in the SciChart.js examples suite: <a href="https://demo.scichart.com/" target="_blank" rel="noreferrer noopener">demo.scichart.com</a></strong></figcaption>
</figure>
</div>
</div>
<div class="clear"> </div>
</div>
<!-- /wp:html -->

<!-- wp:paragraph -->
<p>If you haven't read already, at <a href="https://www.scichart.com">SciChart</a> we're developing a <a href="https://www.scichart.com/scichart-js-update-fast-realtime-javascript-charts/">Javascript chart component which specialises in fast 2D/3D charts &amp; graphs</a> for your <strong>Javascript</strong>, <strong>Typescript</strong>, <strong>React</strong>, <strong>Angular</strong> and possibly even <strong>Blazor</strong> applications. SciChart already exists on other platforms, such as <a href="https://www.scichart.com/wpf-chart-features">Windows (WPF)</a>, <a href="https://www.scichart.com/ios-chart-features/">iOS</a> and <a href="https://www.scichart.com/android-chart-features/">Android</a> and boasts outstanding performance: able to draw millions of data-points in real-time on mobile, or even <a href="https://www.scichart.com/ten-billion-data-points-tech-demo/">plot billions of data-points</a> 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 <a href="https://www.scichart.com/example/javascript-chart/javascript-3d-lidar-visualization/">lidar-visualisation</a> on an iPad.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>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 <strong>WebAssembly</strong> with <strong>WebGL</strong>. <a href="/scichart-js-fast-javascript-chart-progress-update-april-2020/">In a previous post we talked about our progress</a> toward SciChart.js and showcased some real-time charts, and also showcased some <a href="/scichart-js-fast-javascript-3d-charts-update-june-2020/">Javascript 3D data-visualisation</a> that we're building for web browsers.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><strong>In this post I'm going to show you a very quick demo of the speed &amp; performance of SciChart for Javascript: loading 1 million data-points in under 15 milliseconds. </strong></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>This is a simple test which shows how fast SciChart.js is at loading, processing and drawing data. Results below!</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 id="the-performance-test-source-code">The Performance Test Source Code</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>First of all we have to create the <strong>SciChartSurface</strong>. 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.</p>
<!-- /wp:paragraph -->

<!-- wp:scichart/code-snippet {"code":"// Create the WebAssembly context and SciChartSurfacenconst { wasmContext, sciChartSurface } = await createSciChartSurface(divElementId, WIDTH, HEIGHT);nn// Apply a themensciChartSurface.applyTheme(new SciChartJSDarkTheme());nn// Create an XAxis and add to the chartnconst xAxis = new NumericAxis(wasmContext, {n    axisAlignment: EAxisAlignment.Top,n    visibleRange: new NumberRange(0, 1000000),n    autoRange: EAutoRange.Nevern});nsciChartSurface.xAxes.add(xAxis);n// Create a YAxis and add to the chartnconst yAxis = new NumericAxis(wasmContext, {n    axisAlignment: EAxisAlignment.Left,n    visibleRange: new NumberRange(-5000, 5000),n    autoRange: EAutoRange.Nevern});nsciChartSurface.yAxes.add(yAxis);nn// Create a DataSeries of Xy datanconst dataSeries = new XyDataSeries(wasmContext);n// Create the Line series and set propertiesnconst lineSeries = new FastLineRenderableSeries(wasmContext, { dataSeries, strokeThickness: 2 });n// Add the line series to the chartnsciChartSurface.renderableSeries.add(lineSeries);nlineSeries.stroke = u0022#42b649u0022;nn// Set interactivity modifiers: Zoom, pan, mousewheel zoomnsciChartSurface.chartModifiers.add(n    new ZoomExtentsModifier(), n    new ZoomPanModifier(), n    new MouseWheelZoomModifier());"} -->
<div class="wp-block-scichart-code-snippet">
<pre class="line-numbers code-toolbar language-js">// 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());</pre>
</div>
<!-- /wp:scichart/code-snippet -->

<!-- wp:paragraph -->
<p>This code creates a SciChartSurface, adds the axis and renders the empty chart on the screen. The output so far can be seen below:</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":32993,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large"><img class="wp-image-32993" src="https://www.scichart.com/wp-content/uploads/2020/06/MillionPointDemoTest-1.gif" alt="" />
<figcaption>The empty SciChartSurface. Now to create a click handler which loads some data.</figcaption>
</figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>Next we add a button click handler to load the data and perform the performance test. The click handler code looks like this:</p>
<!-- /wp:paragraph -->

<!-- wp:scichart/code-snippet {"code":"// Buttons for chartnconst loadPoints = () =u003e {n    console.log(u0022load pointsu0022);nn    // Clear staten    dataSeries.clear();n    const newTimeSpans: TTimeSpan[] = [];nn    // Start counting Points generation timen    const generateTimestamp = Date.now();nn    // Generate 1,000,000 points as a Random walk n    const POINTS = 1000000;n    const xValues = new Array(POINTS);n    const yValues = new Array(POINTS);n    let prevYValue = 0;n    for (let i = 0; i u003c POINTS; i++) {n        const curYValue = Math.random() * 10 - 5;n        xValues[i] = i;n        yValues[i] = prevYValue + curYValue;n        prevYValue += curYValue;n    }nn    // Log time: Generating 1M data pointsn    newTimeSpans.push({n        title: u0022Generate 1M Data Pointsu0022,n        durationMs: Date.now() - generateTimestampn    });nn    // Start counting data append timen    const appendTimestamp = Date.now();n    dataSeries.appendRange(xValues, yValues);nn    // Log time: Appending data to SciCHartn    newTimeSpans.push({n        title: u0022Append 1M Data Pointsu0022,n        durationMs: Date.now() - appendTimestampn    });nn    // Subscribe to sciChartSurface.rendered event,n    // and calculate time duration for drawingn    const firstFrameTimestamp = Date.now();n    let frameIndex: number = 0;n    // We're going to measure 10 frames n    const numberFramesToMeasure: number = 10;n    let nextFramesTimestamp: number;n    const handler = () =u003e {n        if (frameIndex === 0) {            n            newTimeSpans.push({n                title: u0022Render the first frameu0022,n                durationMs: Date.now() - firstFrameTimestampn            });n            nextFramesTimestamp = Date.now();n            // sciChartSurface.zoomExtents();n        } else if (frameIndex === numberFramesToMeasure + 1) {            n            newTimeSpans.push({n                title: u0022Render next frames averageu0022,n                durationMs: (Date.now() - nextFramesTimestamp) / numberFramesToMeasuren            });nn            // After 10 frames update the UI and unsubscribe from sciChartSurface.renderedn            updateTimeSpans(newTimeSpans);n            sciChartSurface.rendered.unsubscribe(handler);n        }n        // Trigger a redraw immediately n        setTimeout(sciChartSurface.invalidateElement, 0);n        // Increment frame indexn        frameIndex++;n    };n    sciChartSurface.rendered.subscribe(handler);n};nndocument.getElementById(u0022loadPointsu0022).addEventListener(u0022clicku0022, loadPoints);"} -->
<div class="wp-block-scichart-code-snippet">
<pre class="line-numbers code-toolbar language-js">// Buttons for chart
const loadPoints = () =&gt; {
    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 &lt; 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 = () =&gt; {
        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);</pre>
</div>
<!-- /wp:scichart/code-snippet -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Here are the results!</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":32996,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large"><img class="wp-image-32996" src="https://www.scichart.com/wp-content/uploads/2020/06/MillionPointDemoTest-1-1.gif" alt="" />
<figcaption>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.</figcaption>
</figure>
<!-- /wp:image -->

<!-- wp:heading {"level":3} -->
<h3 id="test-1-first-click-after-showing-a-chart-">Test 1 (first click after showing a chart)</h3>
<!-- /wp:heading -->

<!-- wp:list -->
<ul>
<li>Generate 1M points of random walk: 27ms</li>
<li>Append 1M points to Scichart: 34ms</li>
<li>Render the first frame: 35ms</li>
<li><strong>Total data-append and draw time: 69ms</strong></li>
</ul>
<!-- /wp:list -->

<!-- wp:heading {"level":3} -->
<h3 id="test-2-subsequent-click-">Test 2 (subsequent click)</h3>
<!-- /wp:heading -->

<!-- wp:list -->
<ul>
<li>Generate 1M points of random walk: 28ms</li>
<li>Append 1M points to Scichart: 37ms</li>
<li>Render the first frame: 15ms</li>
<li><strong>Total data-append and draw time: 52ms</strong></li>
</ul>
<!-- /wp:list -->

<!-- wp:heading -->
<h2 id="fully-interactive-big-data-charts">Fully Interactive Big Data Charts</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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:</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":32998,"sizeSlug":"full","linkDestination":"none"} -->
<figure class="wp-block-image size-full"><img class="wp-image-32998" src="https://www.scichart.com/wp-content/uploads/2020/06/scichart-javascript-zoom.gif" alt="" />
<figcaption>SciChart.js allows you to browse big data-sets statically or in real-time and does not pre-process or downsample data.</figcaption>
</figure>
<!-- /wp:image -->

<!-- wp:heading -->
<h2 id="using-emscripten-compiler-options-for-further-performance-improvements">Using Emscripten Compiler Options for further Performance Improvements</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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<strong> I forgot to enable Emscripten compiler options for maximum performance</strong> 😀.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>There's a rundown on what the different compiler options do at the <a href="https://emscripten.org/docs/tools_reference/emcc.html">Emscripten compiler documentation.</a> For the following test I enabled <strong>flag -O3 Maximum Speed optimisation</strong>. This also reduces wasm / WebAssembly file size also making it blazing fast for use on the web. Here is the result:</p>
<!-- /wp:paragraph -->

<!-- wp:image {"id":33000,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large"><img class="wp-image-33000" src="https://www.scichart.com/wp-content/uploads/2020/10/image-6.png" alt="" />
<figcaption>SciChart.js Performance Test: Loading 1 Million Points in under 15 milliseconds. This is the world's fastest Javascript chart!</figcaption>
</figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>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...</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 id="incredible-performance-in-a-javascript-chart">Incredible Performance in a Javascript Chart</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>For example:</p>
<!-- /wp:paragraph -->

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

<!-- wp:paragraph -->
<p>All the above were previously impossible projects, but will be made possible by <strong>SciChart.js - High Performance Realtime Javascript Charts</strong>.</p>
<!-- /wp:paragraph -->

<!-- wp:html -->
{% embed https://www.youtube.com/embed/H9H0FcqKTNY %}
<figure class="kg-card kg-embed-card kg-card-hascaption">
<figcaption>SciChart.js webinar in a bit more detail: ECG, Realtime Stock Charts, 1 Million Points &amp; more!</figcaption>
</figure>
<!-- /wp:html -->

<!-- wp:heading {"level":3} -->
<h3 id="scichart-js-release-date"><strong><strong>SciChart.js Release Date</strong></strong></h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>SciChart.js - our GPU Accelerated 2D/3D <a href="https://www.scichart.com/javascript-chart-features/">JavaScript Chart</a> has now been released! In fact, we have just released a version 2 (November 2021) which is a Huge upgrade to v1.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>If you want to learn more about SciChart.js or start a trial, or test drive the features by looking at our <a href="https://demo.scichart.com/">Js Charts Demo</a>, click the link below.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><!--kg-card-begin: html--><a class="button button-highlight" title="SciChart.js JavaScript Chart Library" href="https://www.scichart.com/javascript-chart-features/" target="_blank" rel="noopener noreferrer">SciChart.js JavaScript Chart Library</a><!--kg-card-end: html--></p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 id="about-scichart-high-performance-realtime-charts"><strong><strong><em><em><em><em>About SciChart: High Performance Realtime Charts</em></em></em></em></strong></strong></h2>
<!-- /wp:heading -->

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

<!-- wp:paragraph -->
<p><em><em><em><em>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 <a href="https://www.scichart.com/contact-us/">contact us</a>. Our friendly, helpful support team will be glad to help!</em></em></em></em></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><!--kg-card-begin: html--><a class="button button-highlight" title="Contact SciChart" href="https://www.scichart.com/contact-us/" target="_blank" rel="noopener noreferrer">Contact Us</a><!--kg-card-end: html--></p>
<!-- /wp:paragraph -->