Skip to content

Commit a51a22d

Browse files
committed
actual webpage
1 parent ec411f4 commit a51a22d

File tree

2 files changed

+158
-0
lines changed

2 files changed

+158
-0
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
# HeartRate
2+
23
An online heart-rate monitor using getUserMedia
4+
5+
<a href="https://gfwilliams.github.io/HeartRate/">Try it out!</a>

index.html

+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
<html>
2+
<head>
3+
<!-- 2016 Gordon Williams, [email protected]
4+
5+
Any copyright is dedicated to the Public Domain.
6+
http://creativecommons.org/publicdomain/zero/1.0/
7+
8+
-->
9+
<meta charset="utf-8">
10+
<meta name="viewport" content="width=320, initial-scale=1">
11+
<title>Online Heart Rate Monitor</title>
12+
</head>
13+
<body>
14+
<p>Allow this website to use your webcam, then place your finger lightly over the camera and wait for the trace to stabilise.</p>
15+
<p>You will have most success when there is light behind your finger.</p>
16+
<video id="v" width="100" height="100" style="display:none"></video>
17+
<canvas id="c" width="100" height="100" style="display:none"></canvas>
18+
<canvas id="g" width="320" height="30"></canvas><br/>
19+
<div id="bpm">--</div>
20+
<p><a href="https://github.com/gfwilliams/HeartRate">GitHub</a></p>
21+
<p>Also, check out <a href="http://www.puck-js.com/">Puck.js</a></p>
22+
<script>
23+
var video, width, height, context, graphCanvas, graphContext, bpm;
24+
var hist = [];
25+
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
26+
27+
var constraints = {video: true, audio:false};
28+
29+
function initialize() {
30+
navigator.mediaDevices.enumerateDevices().then(function(devices) {
31+
devices.forEach(function(device) {
32+
console.log(device.kind + ": " + device.label +
33+
" id = " + device.deviceId/*, JSON.stringify(device,null,2)*/);
34+
if (device.kind=="videoinput" /*&& constraints.video===true*/)
35+
constraints.video = { optional: [{sourceId: device.deviceId}, { fillLightMode: "on" }] };
36+
});
37+
initialize2();
38+
}).catch(function(err) {
39+
console.log(err.name + ": " + err.message);
40+
});
41+
}
42+
43+
function initialize2() {
44+
// The source video.
45+
video = document.getElementById("v");
46+
width = video.width;
47+
height = video.height;
48+
49+
// The target canvas.
50+
var canvas = document.getElementById("c");
51+
context = canvas.getContext("2d");
52+
53+
// The canvas for the graph
54+
graphCanvas = document.getElementById("g");
55+
graphContext = graphCanvas.getContext("2d");
56+
57+
// The bpm meter
58+
bpm = document.getElementById("bpm");
59+
60+
// Get the webcam's stream.
61+
navigator.getUserMedia(constraints, startStream, function () {});
62+
}
63+
64+
function startStream(stream) {
65+
video.src = URL.createObjectURL(stream);
66+
video.play();
67+
// Ready! Let's start drawing.
68+
requestAnimationFrame(draw);
69+
}
70+
71+
function draw() {
72+
var frame = readFrame();
73+
if (frame) {
74+
getIntensity(frame.data);
75+
}
76+
77+
// Wait for the next frame.
78+
requestAnimationFrame(draw);
79+
}
80+
81+
function readFrame() {
82+
try {
83+
context.drawImage(video, 0, 0, width, height);
84+
} catch (e) {
85+
// The video may not be ready, yet.
86+
return null;
87+
}
88+
89+
return context.getImageData(0, 0, width, height);
90+
}
91+
92+
function getIntensity(data) {
93+
var len = data.length;
94+
var sum = 0;
95+
96+
for (var i = 0, j = 0; j < len; i++, j += 4) {
97+
sum += data[j] + data[j+1] + data[j+2];
98+
}
99+
//console.log(sum / len);
100+
hist.push({ bright : sum/len, time : Date.now() });
101+
while (hist.length>graphCanvas.width) hist.shift();
102+
// max and min
103+
var max = hist[0].bright;
104+
var min = hist[0].bright;
105+
hist.forEach(function(v) {
106+
if (v.bright>max) max=v.bright;
107+
if (v.bright<min) min=v.bright;
108+
});
109+
// thresholds for bpm
110+
var lo = min*0.6 + max*0.4;
111+
var hi = min*0.4 + max*0.6;
112+
var pulseAvr = 0, pulseCnt = 0;
113+
// draw
114+
var ctx = graphContext;
115+
ctx.clearRect(0, 0, graphCanvas.width, graphCanvas.height);
116+
ctx.beginPath();
117+
ctx.moveTo(0,0);
118+
hist.forEach(function(v,x) {
119+
var y = graphCanvas.height*(v.bright-min)/(max-min);
120+
ctx.lineTo(x,y);
121+
});
122+
ctx.stroke();
123+
// work out bpm
124+
var isHi = undefined;
125+
var lastHi = undefined;
126+
var lastLo = undefined;
127+
ctx.fillStyle = "red";
128+
hist.forEach(function(v, x) {
129+
if (isHi!=true && v.bright>hi) {
130+
isHi = true;
131+
lastLo = x;
132+
}
133+
if (isHi!=false && v.bright<lo) {
134+
if (lastHi !== undefined && lastLo !== undefined) {
135+
pulseAvr += hist[x].time-hist[lastHi].time;
136+
pulseCnt++;
137+
ctx.fillRect(lastLo,graphCanvas.height-4,lastHi-lastLo,4);
138+
}
139+
isHi = false;
140+
lastHi = x;
141+
}
142+
});
143+
// write bpm
144+
if (pulseCnt) {
145+
var pulseRate = 60000 / (pulseAvr / pulseCnt);
146+
bpm.innerHTML = pulseRate.toFixed(0)+" BPM ("+pulseCnt+" pulses)";
147+
} else {
148+
bpm.innerHTML = "-- BPM";
149+
}
150+
}
151+
152+
addEventListener("DOMContentLoaded", initialize);
153+
</script>
154+
</body>
155+
</html>

0 commit comments

Comments
 (0)