How to Link JavaScript Charts and Synchronise zooming, panning, crosshairs
Tags: javascript, crosshairs, zooming
Published: 2024-03-13 14:19:18

<blockquote>In this blog post, we're going to answer a frequently asked question of <a href="https://www.scichart.com/questions/js/link-two-charts-visibleranges-on-zoom-events">How to Synchronise multiple linked charts</a>, so that zooming, panning, cursor / crosshair and tooltip interactions are duplicated across all linked <a href="https://www.scichart.com/javascript-chart-features/">JavaScript Charts</a>.</blockquote>
<h2>Here's the Requirement</h2>
In many dashboards you might want to "link" two or more time-series charts, so that when either one is zoomed, the link would update the other chart to have the same X-range as the other chart.

Perhaps you are creating a timeline control or visualising other processes which occur over time. Having multiple charts allows you to create a layout like this, but you want the charts to scroll, zoom and pan together, and cursor events to track across all the charts at the same time.

<img class="alignnone wp-image-8206 size-full" src="https://www.scichart.com/wp-content/uploads/2023/12/sync-multiple-charts-plan.png" alt="Synchronising or linking multiple JavaScript Charts" width="691" height="504" />
<h2>How to do this with SciChart.js</h2>
Using SciChart's <a href="https://www.scichart.com/javascript-chart-features/">JavaScript Chart Library</a> it's possible to do this quite easily. There are a number of ways we can create the required layout where all charts zoom and pan together. These are:
<ul>
 	<li>Method #1 - Creating separate charts then grouping them (<a href="https://www.scichart.com/documentation/js/current/webframe.html#Tutorial%2009%20-%20Linking%20Multiple%20Charts.html">linking</a>)</li>
 	<li>Method #2 - Creating a single chart with <a href="https://www.scichart.com/documentation/js/current/Axis%20Layout%20-%20Vertically%20Stacked%20Axis.html#:~:text=The%20Stacked%20Axis%20feature%20in,all%20kinds%20of%20chart%20scenarios.">Vertically Stacked Axis</a></li>
 	<li>Method #3 - Using the <a href="https://www.scichart.com/documentation/js/current/SubChartPositioning.html">SubCharts</a> feature to group multiple charts onto a single canvas.</li>
</ul>
We're going to use Method #1 in this post, as it's the simplest. It should perform well for several charts linked but once you get into large numbers of charts (10 to 20 on page) then one of the other methods may be better.
<h3>Initialising the Charts</h3>
For this method each chart is hosted in its own <code>&lt;div&gt;</code>. For this example we're going to use simple HTML / JS. Chart divs can be placed in HTML or included in the page using React, Vue, Angular. Take a look at the <strong>getting started &amp; boilerplate guides <a href="https://www.scichart.com/getting-started/javascript-chart/">here</a></strong> for more info on how to setup each project type.

For the layout we just need some simple HTML &amp; CSS. Start off with this to place two <code>&lt;div&gt;</code>s in the page which occupy 50% of the height of a parent <code>&lt;div&gt;</code>.
<pre><code class="language-markup">&lt;div id="container"&gt;
  &lt;div id="chart0"&gt;&lt;/div&gt;
  &lt;div id="chart1"&gt;&lt;/div&gt;
&lt;/div&gt;

#container {
  width: 100%;
  height: 100vh;
}

#chart0 {
  width: 100%;
  height: 50%;
  background-color: red;
}

#chart1 {
  width: 100%;
  height: 50%;
  background-color: blue;
}</code></pre>
Next, we want to load in SciChart.js. For this sample we're going to use index.min.js from JSDelivr. Include a <code>&lt;script&gt;</code> tag to load SciChart.js in your HTML page as follows:
<pre><code class="language-markup">&lt;script src="https://cdn.jsdelivr.net/npm/scichart/index.min.js" crossorigin="anonymous"&gt;&lt;/script&gt;</code></pre>
Now, in JavaScript we want to create the two charts. We'll add a single X, Y axis to each chart and a line series, plus some zooming, panning modifiers to the chart.
<pre><code class="language-javascript">const { 
  SciChartSurface, 
  NumericAxis,
  ZoomPanModifier,
  MouseWheelZoomModifier,
  SciChartJSLightTheme,
  FastLineRenderableSeries,
  XyDataSeries,
  RolloverModifier,
  ZoomExtentsModifier
} = SciChart;

async function initSciChart(divId) {
  // create a chart
  const { sciChartSurface, wasmContext } = await SciChartSurface.create(divId, { theme: new SciChartJSLightTheme() });
  
  // Add an X and Y axis
  sciChartSurface.xAxes.add(new NumericAxis(wasmContext, { axisTitle: "X Axis"}));
  sciChartSurface.yAxes.add(new NumericAxis(wasmContext, { axisTitle: "Y Axis"}));
  
  // Create some x, y values to display on the chart
  const xValues = Array.from(Array(100).keys());
  const yValues = xValues.map(x =&gt; Math.sin(x*0.3) * (150-x));
  
  // Add a simple line series
  sciChartSurface.renderableSeries.add(new FastLineRenderableSeries(wasmContext, {
    dataSeries: new XyDataSeries(wasmContext, { xValues, yValues }),
    stroke: "Steelblue", 
    strokeThickness: 3
  }))
  
  // Add zooming, panning, crosshair behaviours
  sciChartSurface.chartModifiers.add(
    new ZoomPanModifier(),
    new MouseWheelZoomModifier(),
    new RolloverModifier(),
    new ZoomExtentsModifier()
  );
  
  return sciChartSurface;
}

// Initialize the two charts
async function createLinkedCharts() {
  const sciChart0 = await initSciChart("chart0");
  const sciChart1 = await initSciChart("chart1");
  
  // TODO: We will add behaviour here to link charts later
}

createLinkedCharts();
</code></pre>
This should result in the following output so far:

<img class="alignnone wp-image-8205 size-full" src="https://www.scichart.com/wp-content/uploads/2023/12/two-linked-javascript-charts.png" alt="Synchronising or linking multiple JavaScript Charts" width="2140" height="1652" />
<h3>Applying the Grouping to Synchronise Charts</h3>
There are three steps which need to be done to link multiple JavaScript charts with SciChart.
<ul>
 	<li>Apply a modifier group to share mouse events across charts</li>
 	<li>Apply xAxis.visibleRange synchronization to ensure the horizontal scaling is sync'd</li>
 	<li>(optional) Apply vertical grouping to synchronise the yAxis sizes across two different charts</li>
</ul>
Update the BLAH function to link the two charts as follows:
<pre><code class="language-javascript">// Initialize the two charts
async function createLinkedCharts() {
  const sciChart0 = await initSciChart("chart0");
  const sciChart1 = await initSciChart("chart1");
  
  // Don't forget to import SciChartVerticalGroup
  const { SciChartVerticalGroup } = SciChart;
  
  // Step 1 Apply modifier group to sync mouse events
  sciChart0.chartModifiers.asArray().forEach(cm =&gt; cm.modifierGroup = "ModifierGroupId");
  sciChart1.chartModifiers.asArray().forEach(cm =&gt; cm.modifierGroup = "ModifierGroupId");
  
  // Step 2 Ensure xAxis ranges synced
  // This additional step stops rounding errors from above mouse-even synchronisation from creeping in
  const xAxis0 = sciChart0.xAxes.get(0);
  const xAxis1 = sciChart1.xAxes.get(0); 
  xAxis0.visibleRangeChanged.subscribe((data1) =&gt; {
    xAxis1.visibleRange = data1.visibleRange;
  });
  xAxis1.visibleRangeChanged.subscribe((data1) =&gt; {
    xAxis0.visibleRange = data1.visibleRange;
  });
  
  // Step 3 Vertical chart group synchronizes axis sizes
  const verticalGroup = new SciChartVerticalGroup();
  verticalGroup.addSurfaceToGroup(sciChart0);
  verticalGroup.addSurfaceToGroup(sciChart1);
  
  // Demonstrate the axis size by modifying one chart's decimal places
  const yAxis0 = sciChart0.yAxes.get(0);
  yAxis0.labelProvider.precision = 4;
}</code></pre>
You should now have the following output:
<p class="codepen" style="height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-height="500" data-theme-id="44333" data-default-tab="result" data-slug-hash="XWOvXvy" data-editable="true" data-user="scichart">See the Pen <a href="https://codepen.io/scichart/pen/XWOvXvy">
Synchronise or Link Multiple JavaScript Charts</a> by SciChart.js Official (<a href="https://codepen.io/scichart">@scichart</a>)
on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2>Dynamically Adding/Removing Charts to the Group</h2>
In the above example we have a static number of charts linked together with synchronised zooming, panning operations. However, a common use-case is to have a dynamic number of charts, where you can add/remove chart panes from a linked group.

This is a topic for a further blog post, however we have a React demo on <a href="https://demo.scichart.com/javascript-sync-multi-chart">Dynamically synchronising multiple charts</a> at the SciChart.js <a href="https://demo.scichart.com">JavaScript Charts Demo</a>.
<h2>Further Reading</h2>
<blockquote>Here’s some further resources you may find helpful related to this topic:
<ul>
 	<li><a href="https://www.scichart.com/documentation/js/current/webframe.html#Tutorial%2009%20-%20Linking%20Multiple%20Charts.html">Tutorial 09 - Linking Multiple JavaScript Charts</a></li>
 	<li><a href="https://demo.scichart.com/javascript-sync-multi-chart">Demo: Dynamically adding / removing linked chart panes</a></li>
 	<li><a href="https://demo.scichart.com/javascript-multi-pane-stock-charts-sync-technique">Demo: Creating a multi-pane stock chart control with linked charts</a></li>
 	<li><a href="https://demo.scichart.com/javascript-server-traffic-dashboard">Demo: Complex dashboard with linked charts of different sizes</a></li>
</ul>
</blockquote>