vitallens 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # vitallens.js
2
2
 
3
- [![NPM Version](https://badge.fury.io/js/vitallens.js.svg)](https://www.npmjs.com/package/vitallens.js)
3
+ [![Tests](https://github.com/Rouast-Labs/vitallens.js/actions/workflows/ci.yml/badge.svg)](https://github.com/Rouast-Labs/vitallens.js/actions/workflows/ci.yml)
4
+ [![NPM Version](https://badge.fury.io/js/vitallens.svg)](https://www.npmjs.com/package/vitallens)
4
5
  [![Website](https://img.shields.io/badge/Website-rouast.com/api-blue.svg?logo=data:image/svg%2bxml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+Cjxzdmcgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDI0IDI0IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zOnNlcmlmPSJodHRwOi8vd3d3LnNlcmlmLmNvbS8iIHN0eWxlPSJmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MjsiPgogICAgPGcgdHJhbnNmb3JtPSJtYXRyaXgoMC4xODc5OTgsMCwwLDAuMTg3OTk4LDIzLjMyOTYsMTIuMjQ1MykiPgogICAgICAgIDxwYXRoIGQ9Ik0wLC0yLjgyOEMwLjMzOSwtMi41OTYgMC42NzQsLTIuMzk3IDEuMDA1LC0yLjIyNkwzLjU2NiwtMTUuODczQzAuMjY5LC0yMy42NTYgLTMuMTc1LC0zMS42MTUgLTkuNjU1LC0zMS42MTVDLTE2LjQ2MiwtMzEuNjE1IC0xNy41NDgsLTIzLjk0MiAtMTkuOTQ3LDAuMzEyQy0yMC40MjEsNS4wODEgLTIxLjAzOCwxMS4zMDggLTIxLjcxMSwxNi4wMzFDLTI0LjAxNiwxMS45NTQgLTI2LjY3NSw2LjU0OSAtMjguNDIsMy4wMDJDLTMzLjQ3OSwtNy4yNzggLTM0LjY2NSwtOS4zOTQgLTM2Ljg4OCwtMTAuNTM0Qy0zOS4wMzMsLTExLjYzOSAtNDAuOTk1LC0xMS41OTEgLTQyLjM3MSwtMTEuNDA4Qy00My4wMzcsLTEzIC00My45NDQsLTE1LjQzMSAtNDQuNjY4LC0xNy4zNjdDLTQ5LjUyOSwtMzAuMzkxIC01MS43NzIsLTM1LjQxMiAtNTYuMDY2LC0zNi40NTNDLTU3LjU2NiwtMzYuODE3IC01OS4xNDYsLTM2LjQ5MSAtNjAuMzk5LC0zNS41NjJDLTYzLjQyOCwtMzMuMzI0IC02NC4wMTYsLTI5LjYwMSAtNjUuNjUsLTIuMzcxQy02Ni4wMTcsMy43NDcgLTY2LjQ5NSwxMS43MTMgLTY3LjA1NiwxNy43NzZDLTY5LjE4MiwxNC4xMDggLTcxLjUyNiw5Ljc4MiAtNzMuMjY5LDYuNTcxQy04MS4wNTgsLTcuNzk0IC04Mi42ODcsLTEwLjQyMiAtODUuNzE5LC0xMS4zMUMtODcuNjQ2LC0xMS44NzcgLTg5LjIyMywtMTEuNjYgLTkwLjQyNSwtMTEuMjQ0Qy05MS4yOTYsLTEzLjM3NCAtOTIuNDM0LC0xNi45NzkgLTkzLjI1NSwtMTkuNTgzQy05Ni42LC0zMC4xODkgLTk4LjYyLC0zNi41ODggLTEwNC4xMzUsLTM2LjU4OEMtMTEwLjQ4NCwtMzYuNTg4IC0xMTAuODQzLC0zMC4zOTEgLTExMi4zNTUsLTQuMzExQy0xMTIuNzA3LDEuNzUgLTExMy4xNjksOS43NDIgLTExMy43NDEsMTUuNTUxQy0xMTYuMywxMS43ODEgLTExOS4yOSw2Ljk3OSAtMTIxLjQ1LDMuNDlMLTEyNC4wOTUsMTcuNTc2Qy0xMTcuNjA3LDI3LjU4NSAtMTE0Ljc2NiwzMC40NTggLTExMS4yMDQsMzAuNDU4Qy0xMDQuNjAzLDMwLjQ1OCAtMTA0LjIyMiwyMy44OTMgLTEwMi42MjEsLTMuNzQ3Qy0xMDIuNDIyLC03LjE3IC0xMDIuMTk3LC0xMS4wNDYgLTEwMS45NDYsLTE0LjcyOUMtOTkuNTUxLC03LjIxNiAtOTguMTkyLC0zLjY4NSAtOTUuNTQxLC0yLjA1Qy05Mi42OTgsLTAuMjk3IC05MC4zOTgsLTAuNTQ3IC04OC44MTMsLTEuMTU3Qy04Ny4wNCwxLjYyOSAtODQuMTExLDcuMDMgLTgxLjg0LDExLjIyQy03MS45NTUsMjkuNDQ2IC02OS4yMDIsMzMuNzM1IC02NC44NDYsMzMuOTc1Qy02NC42NjEsMzMuOTg1IC02NC40OCwzMy45ODkgLTY0LjMwNSwzMy45ODlDLTU4LjA2NCwzMy45ODkgLTU3LjY2MiwyNy4zMDQgLTU1LjkxNywtMS43ODdDLTU1LjYzMSwtNi41MyAtNTUuMywtMTIuMDcgLTU0LjkyNywtMTYuOTQ4Qy01NC41MTIsLTE1Ljg1MiAtNTQuMTI5LC0xNC44MjkgLTUzLjgwMywtMTMuOTU1Qy01MS4wNTYsLTYuNTk0IC01MC4xODcsLTQuNDExIC00OC40NzMsLTMuMDQyQy00NS44NywtMC45NjIgLTQzLjE0OSwtMS4zNjkgLTQxLjczNywtMS42MjhDLTQwLjYwMiwwLjMyOSAtMzguNjY0LDQuMjcxIC0zNy4xNjksNy4zMDZDLTI4LjgyNSwyNC4yNjQgLTI1LjE2OCwzMC42NzMgLTE5LjgxMiwzMC42NzNDLTEzLjE1NSwzMC42NzMgLTEyLjM2MiwyMi42NjYgLTEwLjI0NCwxLjI3MkMtOS42NjMsLTQuNjA2IC04Ljg4MiwtMTIuNDk2IC03Ljk5NiwtMTcuODMxQy02Ljk2MywtMTUuNzI5IC01Ljk1NCwtMTMuMzUgLTUuMzA3LC0xMS44MkMtMy4xNDUsLTYuNzIxIC0yLjAxNywtNC4yMDkgMCwtMi44MjgiIHN0eWxlPSJmaWxsOnJnYigwLDE2NCwyMjQpO2ZpbGwtcnVsZTpub256ZXJvOyIvPgogICAgPC9nPgo8L3N2Zz4K)](https://www.rouast.com/api/)
5
6
  [![Documentation](https://img.shields.io/badge/Docs-docs.rouast.com-blue.svg?logo=data:image/svg%2bxml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+Cjxzdmcgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDI0IDI0IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zOnNlcmlmPSJodHRwOi8vd3d3LnNlcmlmLmNvbS8iIHN0eWxlPSJmaWxsLXJ1bGU6ZXZlbm9kZDtjbGlwLXJ1bGU6ZXZlbm9kZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MjsiPgogICAgPGcgdHJhbnNmb3JtPSJtYXRyaXgoMC4xODc5OTgsMCwwLDAuMTg3OTk4LDIzLjMyOTYsMTIuMjQ1MykiPgogICAgICAgIDxwYXRoIGQ9Ik0wLC0yLjgyOEMwLjMzOSwtMi41OTYgMC42NzQsLTIuMzk3IDEuMDA1LC0yLjIyNkwzLjU2NiwtMTUuODczQzAuMjY5LC0yMy42NTYgLTMuMTc1LC0zMS42MTUgLTkuNjU1LC0zMS42MTVDLTE2LjQ2MiwtMzEuNjE1IC0xNy41NDgsLTIzLjk0MiAtMTkuOTQ3LDAuMzEyQy0yMC40MjEsNS4wODEgLTIxLjAzOCwxMS4zMDggLTIxLjcxMSwxNi4wMzFDLTI0LjAxNiwxMS45NTQgLTI2LjY3NSw2LjU0OSAtMjguNDIsMy4wMDJDLTMzLjQ3OSwtNy4yNzggLTM0LjY2NSwtOS4zOTQgLTM2Ljg4OCwtMTAuNTM0Qy0zOS4wMzMsLTExLjYzOSAtNDAuOTk1LC0xMS41OTEgLTQyLjM3MSwtMTEuNDA4Qy00My4wMzcsLTEzIC00My45NDQsLTE1LjQzMSAtNDQuNjY4LC0xNy4zNjdDLTQ5LjUyOSwtMzAuMzkxIC01MS43NzIsLTM1LjQxMiAtNTYuMDY2LC0zNi40NTNDLTU3LjU2NiwtMzYuODE3IC01OS4xNDYsLTM2LjQ5MSAtNjAuMzk5LC0zNS41NjJDLTYzLjQyOCwtMzMuMzI0IC02NC4wMTYsLTI5LjYwMSAtNjUuNjUsLTIuMzcxQy02Ni4wMTcsMy43NDcgLTY2LjQ5NSwxMS43MTMgLTY3LjA1NiwxNy43NzZDLTY5LjE4MiwxNC4xMDggLTcxLjUyNiw5Ljc4MiAtNzMuMjY5LDYuNTcxQy04MS4wNTgsLTcuNzk0IC04Mi42ODcsLTEwLjQyMiAtODUuNzE5LC0xMS4zMUMtODcuNjQ2LC0xMS44NzcgLTg5LjIyMywtMTEuNjYgLTkwLjQyNSwtMTEuMjQ0Qy05MS4yOTYsLTEzLjM3NCAtOTIuNDM0LC0xNi45NzkgLTkzLjI1NSwtMTkuNTgzQy05Ni42LC0zMC4xODkgLTk4LjYyLC0zNi41ODggLTEwNC4xMzUsLTM2LjU4OEMtMTEwLjQ4NCwtMzYuNTg4IC0xMTAuODQzLC0zMC4zOTEgLTExMi4zNTUsLTQuMzExQy0xMTIuNzA3LDEuNzUgLTExMy4xNjksOS43NDIgLTExMy43NDEsMTUuNTUxQy0xMTYuMywxMS43ODEgLTExOS4yOSw2Ljk3OSAtMTIxLjQ1LDMuNDlMLTEyNC4wOTUsMTcuNTc2Qy0xMTcuNjA3LDI3LjU4NSAtMTE0Ljc2NiwzMC40NTggLTExMS4yMDQsMzAuNDU4Qy0xMDQuNjAzLDMwLjQ1OCAtMTA0LjIyMiwyMy44OTMgLTEwMi42MjEsLTMuNzQ3Qy0xMDIuNDIyLC03LjE3IC0xMDIuMTk3LC0xMS4wNDYgLTEwMS45NDYsLTE0LjcyOUMtOTkuNTUxLC03LjIxNiAtOTguMTkyLC0zLjY4NSAtOTUuNTQxLC0yLjA1Qy05Mi42OTgsLTAuMjk3IC05MC4zOTgsLTAuNTQ3IC04OC44MTMsLTEuMTU3Qy04Ny4wNCwxLjYyOSAtODQuMTExLDcuMDMgLTgxLjg0LDExLjIyQy03MS45NTUsMjkuNDQ2IC02OS4yMDIsMzMuNzM1IC02NC44NDYsMzMuOTc1Qy02NC42NjEsMzMuOTg1IC02NC40OCwzMy45ODkgLTY0LjMwNSwzMy45ODlDLTU4LjA2NCwzMy45ODkgLTU3LjY2MiwyNy4zMDQgLTU1LjkxNywtMS43ODdDLTU1LjYzMSwtNi41MyAtNTUuMywtMTIuMDcgLTU0LjkyNywtMTYuOTQ4Qy01NC41MTIsLTE1Ljg1MiAtNTQuMTI5LC0xNC44MjkgLTUzLjgwMywtMTMuOTU1Qy01MS4wNTYsLTYuNTk0IC01MC4xODcsLTQuNDExIC00OC40NzMsLTMuMDQyQy00NS44NywtMC45NjIgLTQzLjE0OSwtMS4zNjkgLTQxLjczNywtMS42MjhDLTQwLjYwMiwwLjMyOSAtMzguNjY0LDQuMjcxIC0zNy4xNjksNy4zMDZDLTI4LjgyNSwyNC4yNjQgLTI1LjE2OCwzMC42NzMgLTE5LjgxMiwzMC42NzNDLTEzLjE1NSwzMC42NzMgLTEyLjM2MiwyMi42NjYgLTEwLjI0NCwxLjI3MkMtOS42NjMsLTQuNjA2IC04Ljg4MiwtMTIuNDk2IC03Ljk5NiwtMTcuODMxQy02Ljk2MywtMTUuNzI5IC01Ljk1NCwtMTMuMzUgLTUuMzA3LC0xMS44MkMtMy4xNDUsLTYuNzIxIC0yLjAxNywtNC4yMDkgMCwtMi44MjgiIHN0eWxlPSJmaWxsOnJnYigwLDE2NCwyMjQpO2ZpbGwtcnVsZTpub256ZXJvOyIvPgogICAgPC9nPgo8L3N2Zz4K)](https://docs.rouast.com/)
6
7
  [![DOI](http://img.shields.io/:DOI-10.48550/arXiv.2312.06892-blue.svg?style=flat&logo=arxiv)](https://doi.org/10.48550/arXiv.2312.06892)
@@ -8,7 +9,7 @@
8
9
  Estimate vital signs such as heart rate and respiratory rate from video in JavaScript.
9
10
 
10
11
  `vitallens.js` is a JavaScript client for the [**VitalLens API**](https://www.rouast.com/api/), which leverages the same inference engine as our [free iOS app VitalLens](https://apps.apple.com/us/app/vitallens/id6472757649).
11
- Furthermore, it includes fast implementations of several other heart rate estimation methods from video such as `G`, `CHROM`, and `POS`.
12
+ Furthermore, it includes fast implementations of several other heart rate estimation methods from video such as `g`, `chrom`, and `pos`.
12
13
 
13
14
  This library works both in browser environments and in Node.js, and comes with a set of examples for file-based processing and real-time webcam streaming.
14
15
 
@@ -24,16 +25,18 @@ Using a different language or platform? We also have a [Python client](https://g
24
25
 
25
26
  - **Multiple Estimation Methods:**
26
27
  Choose the method that fits your needs:
27
- - **`vitallens`** provides *heart rate*, *respiratory rate*, *pulse waveform*, and *respiratory waveform* estimation. In addition, it returns an estimation confidence for each vital. We are working to support more vital signs in the future.
28
- - **`g`**, **`chrom`**, **`pos`** provides support faster, but less accurate *heart rate* and *pulse waveform* estimation.
29
- - While `vitallens` requires an API Key, `g`, `chrom`, and `pos` do not. [Register on our website to get a free API Key.](https://www.rouast.com/api/)
28
+ - **`vitallens`**: Provides *heart rate*, *respiratory rate*, *pulse waveform*, and *respiratory waveform* estimates with associated confidences. *(Requires an API key - [get one for free on our website](https://www.rouast.com/api/))*
29
+ - **`g`**, **`chrom`**, **`pos`**: Offer faster (but less accurate) *heart rate* and *pulse waveform* estimates. *(No API key required.)*
30
30
 
31
31
  - **Fast Face Detection & ROI Support:**
32
32
  Perform rapid face detection when required—or optionally, pass a global region of interest (ROI) to skip detection for even faster processing.
33
33
 
34
34
  - **Event-Driven API:**
35
35
  Register event listeners to receive real-time updates on estimated vitals.
36
-
36
+
37
+ - **Pre-Built Web Component Widgets:**
38
+ In addition to the core API, vitallens.js provides ready-to-use web components. Use the unified widget (supporting both file and webcam modes) or choose the specialized file-only or webcam-only widget for streamlined integration.
39
+
37
40
  - **TypeScript-Ready:**
38
41
  Written in TypeScript with complete type definitions for enhanced developer experience.
39
42
 
@@ -45,161 +48,293 @@ Please review our [Terms of Service](https://www.rouast.com/api/terms) and [Priv
45
48
 
46
49
  ## Installation
47
50
 
48
- Install vitallens.js via npm:
51
+ ### Node.js
52
+
53
+ Install `vitallens.js` via npm or yarn:
49
54
 
50
55
  ```bash
51
56
  npm install vitallens
57
+ # or
58
+ yarn add vitallens
52
59
  ```
53
60
 
54
- Or using yarn:
61
+ Then use it as follows:
55
62
 
56
- ```bash
57
- yarn add vitallens
63
+ ```js
64
+ import { VitalLens } from 'vitallens';
65
+ const vl = new VitalLens({ method: 'vitallens', apiKey: 'YOUR_API_KEY' });
66
+ const result = await vl.processVideoFile(myVideoFile);
67
+ console.log(result);
58
68
  ```
59
69
 
60
- ## Usage
70
+ ### Browser
61
71
 
62
- ### Importing the Library
72
+ For browser usage, you can either bundle `vitallens.js` with your project or load it directly from a CDN. In addition to the core API, `vitallens.js` also provides pre-built web component widgets. We offer three variants:
63
73
 
64
- #### In Browser (ES Modules)
74
+ - **Unified Widget:** Supports both file and webcam modes with mode toggles.
75
+ - **File-Only Widget:** For processing video files only.
76
+ - **Webcam-Only Widget:** For live webcam streaming only.
65
77
 
66
- Include vitallens.js in your HTML as follows:
78
+ For example, using **jsDelivr**:
67
79
 
68
80
  ```html
69
- <script type="module">
70
- import { VitalLens } from 'vitallens.browser.js';
71
- // Your code here
81
+ <!-- Latest version -->
82
+ <script src="https://cdn.jsdelivr.net/npm/vitallens/dist/vitallens.browser.js"></script>
83
+
84
+ <!-- Or pin a specific version -->
85
+ <script src="https://cdn.jsdelivr.net/npm/vitallens@0.0.1/dist/vitallens.browser.js"></script>
86
+
87
+ <!-- Use with core API -->
88
+ <script>
89
+ // vitallens.js is exposed as a global, for example as window.VitalLens.
90
+ const vl = new VitalLens({ method: 'vitallens', apiKey: 'YOUR_API_KEY' });
91
+ // Suppose myMediaStream and myVideoElement are defined:
92
+ vl.addVideoStream(myMediaStream, myVideoElement);
93
+ vl.addEventListener('vitals', (data) => console.log(data));
94
+ vl.startVideoStream();
72
95
  </script>
73
- ```
74
96
 
75
- #### In Node.js (ESM)
97
+ <!-- Or use our widget -->
98
+ <vitallens-widget api-key="YOUR_API_KEY"></vitallens-widget>
76
99
 
77
- ```js
78
- import { VitalLens } from 'vitallens.esm.js';
79
- // Your code here
80
- ```
81
-
82
- ### Processing a Video File (Node.js Example)
100
+ <!-- Or, to use a specialized widget: -->
101
+ <!-- File-only widget -->
102
+ <vitallens-file-widget api-key="YOUR_API_KEY"></vitallens-file-widget>
83
103
 
84
- ```js
85
- import { VitalLens } from 'vitallens.esm.js';
104
+ <!-- Webcam-only widget -->
105
+ <vitallens-webcam-widget api-key="YOUR_API_KEY"></vitallens-webcam-widget>
106
+ ```
86
107
 
87
- const options = {
88
- method: 'vitallens', // Choose from 'vitallens', 'g', 'chrom', or 'pos'
89
- apiKey: 'YOUR_API_KEY', // Required when using the 'vitallens' method
90
- };
108
+ Alternatively, you can use **unpkg**:
91
109
 
92
- const vitallens = new VitalLens(options);
110
+ ```html
111
+ <script src="https://unpkg.com/vitallens/dist/vitallens.browser.js"></script>
112
+ ```
93
113
 
94
- async function processVideoFile(filePath) {
95
- try {
96
- const result = await vitallens.processFile(filePath);
97
- console.log('Processing complete!', result);
98
- } catch (error) {
99
- console.error('Error processing video:', error);
100
- }
101
- }
114
+ Or **Skypack** if you prefer ES modules:
102
115
 
103
- processVideoFile('./examples/sample_video_1.mp4');
116
+ ```html
117
+ <script type="module">
118
+ import { VitalLens } from 'https://cdn.skypack.dev/vitallens';
119
+ // Continue as above…
120
+ </script>
104
121
  ```
105
122
 
106
- ### Real-Time Vital Estimation (Browser Example)
123
+ ## Configuration Options
107
124
 
108
- Below is a minimal example that uses a webcam stream:
125
+ When creating a new `VitalLens` instance, you can configure various options:
109
126
 
110
- ```html
111
- <!DOCTYPE html>
112
- <html lang="en">
113
- <head>
114
- <meta charset="UTF-8">
115
- <title>vitallens.js Webcam Example</title>
116
- </head>
117
- <body>
118
- <video id="video" autoplay muted playsinline style="width:100%; max-width:600px;"></video>
119
- <script type="module">
120
- import { VitalLens } from 'vitallens.browser.js';
121
-
122
- const options = {
123
- method: 'vitallens', // 'vitallens' requires an API key
124
- apiKey: 'YOUR_API_KEY',
127
+ | Parameter | Description | Default |
128
+ | -------------- | ------------------------------------------------------------------------------------------ | -------------- |
129
+ | `method` | Inference method: `'vitallens'`, `'g'`, `'chrom'`, or `'pos'`. | `'vitallens'` |
130
+ | `apiKey` | API key for the VitalLens API (required for method `'vitallens'`). | `null` |
131
+ | `globalRoi` | Optional region of interest for face detection (object with `{ x0, y0, x1, y1 }`). | `undefined` |
132
+ | `waveformMode` | Optional setting how waveform is returned: `'incremental'`, `'windowed'`, or `'complete'`. | *(see below)* |
133
+
134
+ The default value for `waveformMode` is `windowed` if a stream is being analyzed, and `complete` if a file is being processed. Additional options (e.g., face detection settings, buffering) are available. See [docs](https://docs.rouast.com/) for details.
135
+
136
+ ## Understanding the Estimation Results
137
+
138
+ When you process a video or a MediaStream with `vitallens.js`, the library returns vital sign estimates in a structured object. **vitallens.js is designed to process only a single face** — so you always receive a single result object with the following structure:
139
+
140
+ ```typescript
141
+ export interface VitalLensResult {
142
+ face: {
143
+ // Detected face coordinates for each frame, formatted as [x0, y0, x1, y1].
144
+ coordinates: Array<[number, number, number, number]>;
145
+ // Confidence values for the face per frame.
146
+ confidence: number[];
147
+ // An explanatory note regarding the face detection.
148
+ note: string;
149
+ };
150
+ vital_signs: {
151
+ // Estimated global heart rate.
152
+ heart_rate: {
153
+ // Estimated heart rate value.
154
+ value: number;
155
+ // Unit of the heart rate value.
156
+ unit: string;
157
+ // Overall confidence of the heart rate estimation.
158
+ confidence: number;
159
+ // An explanatory note regarding the estimation.
160
+ note: string;
125
161
  };
126
-
127
- const vitallens = new VitalLens(options);
128
-
129
- async function startVitals() {
130
- try {
131
- const video = document.getElementById('video');
132
- const stream = await navigator.mediaDevices.getUserMedia({
133
- video: { facingMode: 'user' },
134
- audio: false
135
- });
136
- video.srcObject = stream;
137
-
138
- // Add the stream to vitallens.js
139
- await vitallens.addStream(stream, video);
140
-
141
- // Listen for vitals events
142
- vitallens.addEventListener('vitals', (data) => {
143
- console.log('Detected vitals:', data);
144
- });
145
-
146
- // Start processing
147
- vitallens.start();
148
- } catch (error) {
149
- console.error('Error initializing webcam:', error);
150
- }
151
- }
152
-
153
- startVitals();
154
- </script>
155
- </body>
156
- </html>
162
+ // Estimated global respiratory rate.
163
+ respiratory_rate?: {
164
+ // Estimated respiratory rate value (in breaths per minute).
165
+ value: number;
166
+ // Unit of the respiratory rate value.
167
+ unit: string;
168
+ // Overall confidence of the respiratory rate estimation.
169
+ confidence: number;
170
+ // An explanatory note regarding the estimation.
171
+ note: string;
172
+ };
173
+ // Photoplethysmogram (PPG) waveform estimation.
174
+ ppg_waveform: {
175
+ // Estimated PPG waveform data (one value per processed frame).
176
+ data: number[];
177
+ // Unit of the waveform data.
178
+ unit: string;
179
+ // Confidence values for the waveform estimation per frame.
180
+ confidence: number[];
181
+ // An explanatory note regarding the waveform estimation.
182
+ note: string;
183
+ };
184
+ // Respiratory waveform estimation.
185
+ respiratory_waveform?: {
186
+ // Estimated respiratory waveform data (one value per processed frame).
187
+ data: number[];
188
+ // Unit of the waveform data.
189
+ unit: string;
190
+ // Confidence values for the waveform estimation per frame.
191
+ confidence: number[];
192
+ // An explanatory note regarding the waveform estimation.
193
+ note: string;
194
+ };
195
+ };
196
+ // A list of timestamps (one per processed frame).
197
+ time: number[];
198
+ // The frames per second (fps) of the input video.
199
+ fps: number;
200
+ // The effective fps used for inference.
201
+ estFps: number;
202
+ // A message providing additional information about the estimation.
203
+ message: string;
204
+ }
157
205
  ```
158
206
 
159
- ### Configuration Options
207
+ ## Examples
160
208
 
161
- When creating a new `VitalLens` instance, you can configure various options:
209
+ Before running any of the examples, make sure to build the project by executing:
162
210
 
163
- | Parameter | Description | Default |
164
- | ------------- | ------------------------------------------------------------------------------------------------ | ------------- |
165
- | `method` | Inference method: `'vitallens'`, `'G'`, `'CHROM'`, or `'POS'`. | `'vitallens'` |
166
- | `apiKey` | API key for the VitalLens API (required for method `'vitallens'`). | `null` |
167
- | `globalRoi` | Optional region of interest for face detection (object with `{ x0, y0, x1, y1 }`). | `undefined` |
168
- | *Others* | Additional options (e.g., face detection settings, buffering) are available. See [docs](https://docs.rouast.com/) for details. | |
211
+ ```bash
212
+ npm run build
213
+ ```
169
214
 
170
- ## Examples
215
+ Also, note that each example requires an API key. Replace `YOUR_API_KEY` with your actual API key when running the examples.
171
216
 
172
- The repository contains several ready-to-run examples:
217
+ - **Browser Unified Widget:**
218
+ [examples/browser/widget.html](examples/browser/widget.html)
219
+ To run this example, execute:
220
+ ```bash
221
+ API_KEY=YOUR_API_KEY npm run start:browser
222
+ ```
173
223
 
174
- - **Browser File Input:**
175
- [examples/browser/file.html](examples/browser/file.html)
224
+ - **Browser File Input Widget:**
225
+ [examples/browser/file_widget.html](examples/browser/file_widget.html)
176
226
  To run this example, execute:
177
227
  ```bash
178
- npm run start:browser-file
228
+ API_KEY=YOUR_API_KEY npm run start:browser-file
179
229
  ```
180
230
 
181
- - **Minimal Webcam Example:**
182
- [examples/browser/webcam_minimal.html](examples/browser/webcam_minimal.html)
231
+ - **Browser File Input Minimal:**
232
+ [examples/browser/file_minimal.html](examples/browser/file_minimal.html)
233
+ To run this example, execute:
234
+ ```bash
235
+ API_KEY=YOUR_API_KEY npm run start:browser-file-minimal
236
+ ```
237
+
238
+ - **Browser Webcam Input Widget:**
239
+ [examples/browser/webcam_widget.html](examples/browser/webcam_widget.html)
183
240
  To run this example, execute:
184
241
  ```bash
185
- npm run start:browser-webcam-minimal
242
+ API_KEY=YOUR_API_KEY npm run start:browser-webcam
186
243
  ```
187
244
 
188
- - **Advanced Webcam with Visualizations:**
189
- [examples/browser/webcam.html](examples/browser/webcam.html)
245
+ - **Browser Webcam Input Minimal:**
246
+ [examples/browser/webcam_minimal.html](examples/browser/webcam_minimal.html)
190
247
  To run this example, execute:
191
248
  ```bash
192
- npm run start:browser-webcam
249
+ API_KEY=YOUR_API_KEY npm run start:browser-webcam-minimal
193
250
  ```
194
251
 
195
252
  - **Node File Processing:**
196
253
  [examples/node/file.js](examples/node/file.js)
197
254
  To run this example, execute:
198
255
  ```bash
199
- npm run start:node-file
256
+ API_KEY=YOUR_API_KEY npm run start:node-file
200
257
  ```
201
258
 
202
- Try opening the HTML examples in your browser or running the Node script to see vitallens.js in action.
259
+ Try opening the HTML examples in your browser or running the Node script to see `vitallens.js` in action.
260
+
261
+ ## Securing your API Key
262
+
263
+ For security reasons, we recommend that you do not expose your API key directly in client-side code. There are two primary approaches to secure your API key:
264
+
265
+ ### 1. Run Everything on your Server
266
+
267
+ If you are building a server-side application using Node.js, your API key remains securely on your server. Simply call the API directly from your backend code without exposing your credentials.
268
+
269
+ ### 2. Use a Proxy Server for Client-Side Code
270
+
271
+ If you need to use `vitallens.js` in a browser, you can set up a proxy server. The proxy server receives requests from the client, attaches your API key (stored securely on the server), and forwards the request to the VitalLens API. This way, the API key is never exposed to the client.
272
+
273
+ Our client library supports this by accepting a `proxyUrl` option. For example:
274
+
275
+ ```js
276
+ import { VitalLens } from 'vitallens';
277
+ const vl = new VitalLens({
278
+ method: 'vitallens',
279
+ proxyUrl: 'https://your-proxy-server.com/api' // URL to your deployed proxy server
280
+ });
281
+ ```
282
+
283
+ Or when using one of our widgets:
284
+
285
+ ```html
286
+ <vitallens-widget proxy-url="https://your-proxy-server.com/api"></vitallens-widget>
287
+ ```
288
+
289
+ ### Sample Proxy Server Implementation
290
+
291
+ Below is a simple Node.js/Express proxy server implementation that you can use as a starting point:
292
+
293
+ ```js
294
+ const express = require('express');
295
+ const bodyParser = require('body-parser');
296
+ const cors = require('cors');
297
+
298
+ const app = express();
299
+ const PORT = process.env.PORT || 3000;
300
+
301
+ // Securely store your API key in an environment variable
302
+ const API_KEY = process.env.VITALLENS_API_KEY;
303
+ const VITALLENS_ENDPOINT = 'https://api.rouast.com/vitallens-v2';
304
+
305
+ app.use(bodyParser.json({ limit: '10mb' }));
306
+
307
+ // Enable CORS for your allowed domain.
308
+ app.use(cors({
309
+ origin: 'http://example.com', // Your allowed domain
310
+ methods: ['GET', 'POST', 'OPTIONS'],
311
+ allowedHeaders: ['Content-Type', 'Authorization']
312
+ }));
313
+
314
+ app.post('/', async (req, res) => {
315
+ try {
316
+ const response = await fetch(VITALLENS_ENDPOINT, {
317
+ method: 'POST',
318
+ headers: {
319
+ 'Content-Type': 'application/json',
320
+ 'x-api-key': API_KEY,
321
+ },
322
+ body: JSON.stringify(req.body),
323
+ });
324
+ const data = await response.text();
325
+ res.status(response.status).send(data);
326
+ } catch (error) {
327
+ console.error('Proxy error:', error);
328
+ res.status(500).send('Internal server error');
329
+ }
330
+ });
331
+
332
+ app.listen(PORT, () => {
333
+ console.log(`Proxy server listening on port ${PORT}`);
334
+ });
335
+ ```
336
+
337
+ You can deploy this proxy server on any Node.js hosting platform (such as Heroku, Vercel, or your own server) and then set the URL as the `proxyUrl` in your VitalLens client configuration.
203
338
 
204
339
  ## Development
205
340
 
@@ -226,6 +361,8 @@ For environment-specific tests, you can use:
226
361
  ```bash
227
362
  npm run test:browser
228
363
  npm run test:node
364
+ npm run test:browser-integration
365
+ npm run test:node-integration
229
366
  ```
230
367
 
231
368
  ### Linting
@@ -1,3 +1,6 @@
1
+ import './web-components/VitalLensWidgetUnified';
2
+ import './web-components/VitalLensWidgetFile';
3
+ import './web-components/VitalLensWidgetWebcam';
1
4
  export { VitalLens } from './core/VitalLens.browser';
2
5
  export type { VitalLensOptions, VitalLensResult } from './types/core';
3
6
  export { Frame } from './processing/Frame';