Angular Vital Signs ECG/EKG Medical Demo

Showcases how SciChart.js can be used in a Medical context, drawing ECGs with our High Performance JavaScript Charts

ECG
0
V1 - 1.4MM
ST | +0.6 || +0.9
NIBP
AUTO
145/95
0/0
SV
ML 100
%**** 55
0.0
SPO2
18:06
71-
RESP
0

Fullscreen

Edit

 Edit

Docs

drawExample.ts

angular.ts

theme.ts

vitalSignsEcgData.ts

Copy to clipboard
Minimise
Fullscreen
1import {
2    CategoryAxis,
3    EllipsePointMarker,
4    EventHandler,
5    FastLineRenderableSeries,
6    NumberRange,
7    NumericAxis,
8    RightAlignedOuterVerticallyStackedAxisLayoutStrategy,
9    SciChartSurface,
10    XyDataSeries,
11} from "scichart";
12import { vitalSignsEcgData } from "./data/vitalSignsEcgData";
13import { appTheme } from "../../../theme";
14
15const STEP = 10;
16const TIMER_TIMEOUT_MS = 20;
17const STROKE_THICKNESS = 4;
18const POINTS_LOOP = 5200;
19const GAP_POINTS = 50;
20const DATA_LENGTH = vitalSignsEcgData.xValues.length;
21
22const { ecgHeartRateValues, bloodPressureValues, bloodVolumeValues, bloodOxygenationValues } = vitalSignsEcgData;
23
24// HELPER FUNCTIONS
25const getValuesFromData = (xIndex: number) => {
26    const xArr: number[] = [];
27    const ecgHeartRateArr: number[] = [];
28    const bloodPressureArr: number[] = [];
29    const bloodVolumeArr: number[] = [];
30    const bloodOxygenationArr: number[] = [];
31    for (let i = 0; i < STEP; i++) {
32        const dataIndex = (xIndex + i) % DATA_LENGTH;
33        const x = xIndex + i;
34        xArr.push(x);
35        ecgHeartRateArr.push(ecgHeartRateValues[dataIndex]);
36        bloodPressureArr.push(bloodPressureValues[dataIndex]);
37        bloodVolumeArr.push(bloodVolumeValues[dataIndex]);
38        bloodOxygenationArr.push(bloodOxygenationValues[dataIndex]);
39    }
40    return {
41        xArr,
42        ecgHeartRateArr,
43        bloodPressureArr,
44        bloodVolumeArr,
45        bloodOxygenationArr,
46    };
47};
48
49export type TDataUpdateInfo = {
50    ecg: number;
51    bloodPressure1: number;
52    bloodPressure2: number;
53    bloodVolume: number;
54    bloodOxygenation: number;
55};
56
57// SCICHART
58export const drawExample = async (rootElement: string | HTMLDivElement) => {
59    const { sciChartSurface, wasmContext } = await SciChartSurface.create(rootElement, {
60        theme: appTheme.SciChartJsTheme,
61    });
62
63    // Create a single, shared X-axis, pre-sized to fit the data in X, and is invisible
64
65    // Note: For fifoSweeping mode to work, the X-Axis must be a CategoryAxis
66    //      NumericAxis is also supported, but x-values must then be offsets from 0, ie do x % fifoCapacity.
67    //      See more info in the docs
68    const xAxis = new CategoryAxis(wasmContext, {
69        visibleRange: new NumberRange(0, POINTS_LOOP),
70        isVisible: false,
71    });
72    sciChartSurface.xAxes.add(xAxis);
73
74    // Create multiple y-axis, one per trace. Using the stacked vertically layout strategy
75    const yAxisHeartRate = new NumericAxis(wasmContext, {
76        id: "yHeartRate",
77        visibleRange: new NumberRange(0.7, 1.0),
78        isVisible: false,
79    });
80    const yAxisBloodPressure = new NumericAxis(wasmContext, {
81        id: "yBloodPressure",
82        visibleRange: new NumberRange(0.4, 0.8),
83        isVisible: false,
84    });
85    const yAxisBloodVolume = new NumericAxis(wasmContext, {
86        id: "yBloodVolume",
87        visibleRange: new NumberRange(0.1, 0.5),
88        isVisible: false,
89    });
90    const yAxisBloodOxygenation = new NumericAxis(wasmContext, {
91        id: "yBloodOxygenation",
92        visibleRange: new NumberRange(0, 0.2),
93        isVisible: false,
94    });
95    sciChartSurface.layoutManager!.rightOuterAxesLayoutStrategy =
96        new RightAlignedOuterVerticallyStackedAxisLayoutStrategy();
97    sciChartSurface.yAxes.add(yAxisHeartRate, yAxisBloodPressure, yAxisBloodVolume, yAxisBloodOxygenation);
98
99    // Using the NEW fifoCapacity, fifoSweeping mode in SciChart.js v3.2 we specify a number of points
100    // we want in the viewport. When the right edge of the viewport is reached, the series wraps around
101
102    const fifoSweepingGap = GAP_POINTS;
103    const dataSeries1 = new XyDataSeries(wasmContext, {
104        fifoCapacity: POINTS_LOOP,
105        fifoSweeping: true,
106        fifoSweepingGap,
107    });
108    const dataSeries2 = new XyDataSeries(wasmContext, {
109        fifoCapacity: POINTS_LOOP,
110        fifoSweeping: true,
111        fifoSweepingGap,
112    });
113    const dataSeries3 = new XyDataSeries(wasmContext, {
114        fifoCapacity: POINTS_LOOP,
115        fifoSweeping: true,
116        fifoSweepingGap,
117    });
118    const dataSeries4 = new XyDataSeries(wasmContext, {
119        fifoCapacity: POINTS_LOOP,
120        fifoSweeping: true,
121        fifoSweepingGap,
122    });
123
124    // A pointmarker with lastPointOnly = true will be used for all series to mark the last point
125    const pointMarkerOptions = {
126        width: 7,
127        height: 7,
128        strokeThickness: 2,
129        fill: appTheme.MutedSkyBlue,
130        lastPointOnly: true,
131    };
132
133    // Create four RenderableSeries which render the data
134    sciChartSurface.renderableSeries.add(
135        new FastLineRenderableSeries(wasmContext, {
136            yAxisId: yAxisHeartRate.id,
137            strokeThickness: STROKE_THICKNESS,
138            stroke: appTheme.VividOrange,
139            dataSeries: dataSeries1,
140            pointMarker: new EllipsePointMarker(wasmContext, { ...pointMarkerOptions, stroke: appTheme.VividOrange }),
141        })
142    );
143
144    sciChartSurface.renderableSeries.add(
145        new FastLineRenderableSeries(wasmContext, {
146            yAxisId: yAxisBloodPressure.id,
147            strokeThickness: STROKE_THICKNESS,
148            stroke: appTheme.VividSkyBlue,
149            dataSeries: dataSeries2,
150            pointMarker: new EllipsePointMarker(wasmContext, { ...pointMarkerOptions, stroke: appTheme.VividSkyBlue }),
151        })
152    );
153
154    sciChartSurface.renderableSeries.add(
155        new FastLineRenderableSeries(wasmContext, {
156            yAxisId: yAxisBloodVolume.id,
157            strokeThickness: STROKE_THICKNESS,
158            stroke: appTheme.VividPink,
159            dataSeries: dataSeries3,
160            pointMarker: new EllipsePointMarker(wasmContext, { ...pointMarkerOptions, stroke: appTheme.VividPink }),
161        })
162    );
163
164    sciChartSurface.renderableSeries.add(
165        new FastLineRenderableSeries(wasmContext, {
166            yAxisId: yAxisBloodOxygenation.id,
167            strokeThickness: STROKE_THICKNESS,
168            stroke: appTheme.VividTeal,
169            dataSeries: dataSeries4,
170            pointMarker: new EllipsePointMarker(wasmContext, { ...pointMarkerOptions, stroke: appTheme.VividTeal }),
171        })
172    );
173
174    const dataUpdateEventHandler = new EventHandler<TDataUpdateInfo>();
175
176    let timerId: NodeJS.Timeout;
177    let currentPoint = 0;
178
179    // The following code is run once per timer-step to update the data in the charts
180    // Here you would subsitute your own callback to receive data from your data feed or sensors
181    const runUpdateDataOnTimeout = () => {
182        // Get data
183        const { xArr, ecgHeartRateArr, bloodPressureArr, bloodVolumeArr, bloodOxygenationArr } =
184            getValuesFromData(currentPoint);
185        currentPoint += STEP;
186
187        // appendRange when fifoSweepingMode = true and fifoCapacity is reached will cause the series to wrap around
188        dataSeries1.appendRange(xArr, ecgHeartRateArr);
189        dataSeries2.appendRange(xArr, bloodPressureArr);
190        dataSeries3.appendRange(xArr, bloodVolumeArr);
191        dataSeries4.appendRange(xArr, bloodOxygenationArr);
192
193        // Update Info panel
194        if (currentPoint % 1000 === 0) {
195            const ecg = ecgHeartRateArr[STEP - 1];
196            const bloodPressure = bloodPressureArr[STEP - 1];
197            const bloodVolume = bloodVolumeArr[STEP - 1] + 3;
198            const bloodOxygenation = bloodOxygenationArr[STEP - 1];
199
200            const dataUpdateInfo = {
201                ecg: Math.floor(ecg * 20),
202                bloodPressure1: Math.floor(bloodPressure * 46),
203                bloodPressure2: Math.floor(bloodPressure * 31),
204                bloodVolume: bloodVolume + 8.6,
205                bloodOxygenation: Math.floor(bloodOxygenation * 10 + 93),
206            };
207            dataUpdateEventHandler.raiseEvent(dataUpdateInfo);
208        }
209        timerId = setTimeout(runUpdateDataOnTimeout, TIMER_TIMEOUT_MS);
210    };
211
212    const subscribeToDataUpdates = (handler: (info: TDataUpdateInfo) => void) => {
213        dataUpdateEventHandler.subscribe(handler);
214
215        // automatically cleanup subscription whe surface is deleted
216        sciChartSurface.addDeletable({ delete: () => dataUpdateEventHandler.unsubscribeAll() });
217    };
218
219    const stopUpdate = () => {
220        clearTimeout(timerId);
221        timerId = undefined;
222    };
223
224    const startUpdate = () => {
225        if (timerId) {
226            stopUpdate();
227        }
228        runUpdateDataOnTimeout();
229    };
230
231    return { sciChartSurface, subscribeToDataUpdates, controls: { startUpdate, stopUpdate } };
232};
233

Vital Signs Monitor Demo - Angular

Overview

This example demonstrates an Angular implementation of a real-time medical monitoring chart using SciChart.js. The demo visualizes multiple vital signs data channels including ECG, blood pressure, blood volume, and blood oxygenation. By leveraging the ScichartAngularComponent provided by scichart-angular, it delivers high-performance chart rendering suitable for demanding real-time applications.

Technical Implementation

The Angular component initializes the SciChartSurface using SciChart.js’s API, configuring multiple axes and renderable series to display the various vital signs. Key aspects include the use of Angular lifecycle hooks as detailed in Component Lifecycle - Angular to manage initialization and cleanup, and dynamic styling through Angular’s ngStyle to create responsive info panels. Real-time data updates are implemented via a timer-based update mechanism with an event subscription model, ensuring that all data series and their corresponding info displays remain synchronized.

Features and Capabilities

The demo supports real-time chart updates, dynamically plotting data from multiple channels with advanced WebGL rendering for smooth, high-frequency refreshes. It manages four separate renderable series, each corresponding to different vital signs, and updates detailed info panels using the Vertically Stacked Axis feature to reflect the latest measurements continuously. The use of dataSeries with fifoCapacity and fifoSweeping mode enable wrap-around charts, which is common when rendering ECG style applications. This provides an engaging and immediate visualization experience crucial for medical monitoring scenarios.

Integration and Best Practices

The implementation follows Angular best practices; it effectively incorporates Angular dependency injection and lifecycle management to integrate with third-party libraries like SciChart.js. Developers are encouraged to review Getting Started with SciChart JS and Tutorial 01 - Setting up a npm Project with SciChart.js for guidance on initial setup. Moreover, by utilizing robust event handling and efficient subscription management—as is further explained in Performance Optimization Techniques in Angular—this demo illustrates how Angular applications can integrate third-party tools effectively while maintaining optimal performance in a real-time data environment.

angular Chart Examples & Demos

See Also: Scientific & Medical Charts (10 Demos)

Angular Chart with Logarithmic Axis Example | SciChart.js

Angular Chart with Logarithmic Axis Example

Demonstrates Logarithmic Axis on a Angular Chart using SciChart.js. SciChart supports logarithmic axis with scientific or engineering notation and positive and negative values

LiDAR 3D Point Cloud of Geospatial Data | SciChart.js

LiDAR 3D Point Cloud of Geospatial Data

Demonstrating the capability of SciChart.js to create JavaScript 3D Point Cloud charts and visualize LiDAR data from the UK Defra Survey.

Angular Chart with Vertically Stacked Axes | SciChart.js

Angular Chart with Vertically Stacked Axes

Demonstrates Vertically Stacked Axes on a Angular Chart using SciChart.js, allowing data to overlap

Realtime Audio Spectrum Analyzer Chart | SciChart.js Demo

Realtime Audio Spectrum Analyzer Chart Example

See the frequency of recordings with the Angular audio spectrum analyzer example from SciChart. This real-time audio visualizer demo uses a Fourier Transform.

Realtime Audio Analyzer Bars Demo | SciChart.js Demo

Realtime Audio Analyzer Bars Demo

Demonstrating the capability of SciChart.js to create a JavaScript Audio Analyzer Bars and visualize the Fourier-Transform of an audio waveform in realtime.

Interactive Waterfall Chart | Angular Charts | SciChart.js

Interactive Waterfall Spectral Chart

Demonstrates how to create a Waterfall chart in SciChart.js, showing chromotragraphy data with interactive selection of points.

Interactive Phasor Diagram chart | Angular Charts | SciChart.js

Phasor Diagram Chart Example

See the Angular Phasor Diagram example to combine a Cartesian surface with a Polar subsurface. Get seamless Angular integration with SciChart. View demo now.

NEW!
Angular Correlation Plot | Angular Charts | SciChart.js Demo

Angular Correlation Plot

Create Angular Correlation Plot with high performance SciChart.js. Easily render pre-defined point types. Supports custom shapes. Get your free trial now.

NEW!
Angular Semiconductors Dashboard | JavaScript Charts | SciChart.js

Angular Semiconductors Dashboard

Angular **Semiconductors Dashboard** using SciChart.js, by leveraging the **FastRectangleRenderableSeries**, and its `customTextureOptions` property to have a custom tiling texture fill.

NEW!
Angular Wafer Analysis Chart | JavaScript Charts | SciChart.js

Angular Wafer Analysis Chart

Angular **Wafer Analysis Chart** using SciChart.js, by leveraging the **FastRectangleRenderableSeries**, and crossfilter to enable live filtering.

SciChart Ltd, 16 Beaufort Court, Admirals Way, Docklands, London, E14 9XL.