This example demonstrates how to synchronise layout and visible range across multiple dynamic charts, and how to synchronise series with an overview chart.using SciChart.js, High Performance JavaScript Charts
Click and drag or mousewheel to zoom/pan the charts.
drawExample.ts
index.tsx
RandomWalkGenerator.ts
theme.ts
AxisSynchroniser.ts
1import {
2 EAutoRange,
3 EAxisAlignment,
4 EExecuteOn,
5 FastLineRenderableSeries,
6 IRenderableSeries,
7 MouseWheelZoomModifier,
8 NumberRange,
9 NumericAxis,
10 OverviewRangeSelectionModifier,
11 RolloverModifier,
12 RubberBandXyZoomModifier,
13 SciChartSurface,
14 XyDataSeries,
15 ZoomExtentsModifier,
16 ZoomPanModifier,
17 buildSeries,
18} from "scichart";
19import { RandomWalkGenerator } from "../../../ExampleData/RandomWalkGenerator";
20import { appTheme } from "../../../theme";
21import { AxisSynchroniser } from "./AxisSynchroniser";
22
23export const createChart = async (divId: string, id: number) => {
24 const { wasmContext, sciChartSurface } = await SciChartSurface.create(divId, {
25 theme: appTheme.SciChartJsTheme,
26 disableAspect: true,
27 });
28
29 // Create and add an XAxis and YAxis
30 sciChartSurface.xAxes.add(new NumericAxis(wasmContext, {}));
31 sciChartSurface.yAxes.add(
32 new NumericAxis(wasmContext, {
33 autoRange: EAutoRange.Always,
34 growBy: new NumberRange(0.1, 0.1),
35 axisAlignment: EAxisAlignment.Left,
36 })
37 );
38
39 const stroke = appTheme.SciChartJsTheme.getStrokeColor(id, 5, wasmContext);
40 const POINTS = 1000;
41 const data0 = new RandomWalkGenerator().Seed((id + 1) * 10).getRandomWalkSeries(POINTS);
42 sciChartSurface.renderableSeries.add(
43 new FastLineRenderableSeries(wasmContext, {
44 dataSeries: new XyDataSeries(wasmContext, { xValues: data0.xValues, yValues: data0.yValues }),
45 strokeThickness: 3,
46 stroke,
47 })
48 );
49
50 // Use modifierGroup to trigger the modifier in the same place on multiple charts
51 sciChartSurface.chartModifiers.add(
52 new RolloverModifier({ modifierGroup: "one" }),
53 new RubberBandXyZoomModifier({
54 executeCondition: { button: EExecuteOn.MouseRightButton },
55 modifierGroup: "one",
56 }),
57 // These do not need modifierGroup as the xAxes are synchronised.
58 new ZoomPanModifier({ enableZoom: true }),
59 new ZoomExtentsModifier(),
60 new MouseWheelZoomModifier()
61 );
62
63 return { sciChartSurface, wasmContext };
64};
65
66export const createOverview = async (divId: string, axisSynchroniser: AxisSynchroniser) => {
67 // Note this does not use SciChartOverview.
68 // Instead we create a normal chart and then manually add the OverviewRangeSelectionModifier and bind it to the axisSynchroniser
69 const { wasmContext, sciChartSurface } = await SciChartSurface.create(divId, {
70 theme: appTheme.SciChartJsTheme,
71 });
72
73 // Create and add an XAxis and YAxis
74 const xAxis = new NumericAxis(wasmContext, { visibleRange: new NumberRange(0, 1000), autoRange: EAutoRange.Never });
75 sciChartSurface.xAxes.add(xAxis);
76 sciChartSurface.yAxes.add(
77 new NumericAxis(wasmContext, {
78 autoRange: EAutoRange.Always,
79 growBy: new NumberRange(0.1, 0.1),
80 axisAlignment: EAxisAlignment.Left,
81 })
82 );
83
84 const rangeSelectionModifier = new OverviewRangeSelectionModifier();
85 // When the range selection is moved, updated the linked charts
86 rangeSelectionModifier.onSelectedAreaChanged = (selectedRange: NumberRange) => {
87 if (!selectedRange.equals(axisSynchroniser.visibleRange)) {
88 axisSynchroniser.publishChange({ visibleRange: selectedRange }, xAxis);
89 }
90 };
91 rangeSelectionModifier.selectedArea = axisSynchroniser.visibleRange;
92 sciChartSurface.chartModifiers.add(rangeSelectionModifier);
93
94 // When charts are moved, update the range selection
95 axisSynchroniser.visibleRangeChanged.subscribe(({ visibleRange }) => {
96 const updatedSelectedRange = visibleRange.clip(xAxis.visibleRange);
97 const shouldUpdateSelectedRange = !updatedSelectedRange.equals(rangeSelectionModifier.selectedArea);
98 if (shouldUpdateSelectedRange) {
99 rangeSelectionModifier.selectedArea = updatedSelectedRange;
100 }
101 });
102 return { wasmContext, sciChartSurface };
103};
104
105export const addToOverview = (series: IRenderableSeries, overview: SciChartSurface) => {
106 // Deep clone the series but without the data
107 const cloneSeries = buildSeries(overview.webAssemblyContext2D, series.toJSON(true))[0];
108 // Reference the original data
109 cloneSeries.dataSeries = series.dataSeries;
110 // Clear the axisIds so the series will automatically be assigned to the default axes on the overview.
111 // in v4 we can no longer rely on the axisIds being DefaultAxisId
112 cloneSeries.xAxisId = undefined;
113 cloneSeries.yAxisId = undefined;
114 overview.renderableSeries.add(cloneSeries);
115};
116
117export const removeFromOverview = (series: IRenderableSeries, overview: SciChartSurface) => {
118 const overviewSeries = overview.renderableSeries.getById(series.id);
119 // Do not delete children as this is using shared data
120 overview.renderableSeries.remove(overviewSeries, false);
121};
122This example, "Synchronise Multiple Charts", demonstrates how to synchronise layout, visible ranges, and data series across multiple dynamic chart components in an Angular application using SciChart.js. The implementation focuses on coordinating zoom, pan, and overview series updates among various charts to deliver a consistent and responsive dashboard experience.
The core of the implementation is a custom AxisSynchroniser class that manages the visible range state across multiple X axes. Each chart’s axis subscribes to range change events, and any update is propagated to all linked charts. The charts are created dynamically by invoking SciChart.js methods such as SciChartSurface.create() and instantiating axes like NumericAxis, without the use of a Builder API, ensuring that every chart is independently rendered and easily synchronised. This approach also leverages Angular’s component lifecycle techniques (similar to ngOnInit and ngOnDestroy as explained in the Angular Component Lifecycle) to handle proper initialisation and cleanup, thereby avoiding memory leaks as detailed in the Memory Best Practices documentation.
The example supports real-time updating of charts with dynamic data, synchronized zooming and panning, and an interactive overview chart that controls the visible range of the main charts. Users can dynamically add or remove charts, and toggle synchronization using built-in UI controls. The custom implementation seamlessly links the primary charts with an overview component by copying series data and manually managing event handlers for range updates. This interactivity and custom synchronisation mechanism reflect advanced features of SciChart.js and provide a robust foundation for developing high-performance, multi-chart Angular dashboards.
Designed specifically for Angular, this example follows best practices for integration by utilising Angular Material and Flexbox for layout management, ensuring a fully responsive multi-chart dashboard as described in resources like Angular Material and CSS Flex Layout. Additionally, efficient resource management is demonstrated through rigorous cleanup routines and controlled event subscription handling, principles that are essential in Angular applications. For organizing chart synchronisation logic, the use of dependency injection with Angular services can further modularise and enhance maintainability, aligning with the strategies outlined in Angular Services and Dependency Injection Explained. Finally, developers interested in advanced synchronisation techniques might find it beneficial to review the strategies provided in the Tutorial 09 - Linking Multiple Charts for a deeper technical context.

Create a Angular Multi-Pane Candlestick / Stock Chart with indicator panels, synchronized zooming, panning and cursors. Get your free trial of SciChart.js now.

This dashboard demo showcases the incredible realtime performance of our Angular charts by updating the series with millions of data-points!