Build a Simple Motion Detection Demo with WebRTC and Canvas
This guide explains how to create a lightweight motion‑detection effect in a web page using WebRTC to capture video, Canvas for pixel processing, and JavaScript algorithms that compare consecutive frames, apply brightness thresholds, and optionally track moving objects with bounding boxes.
Video source
The implementation uses the WebRTC navigator.mediaDevices.getUserMedia() API to obtain a live video stream from the user's camera, avoiding Flash or Silverlight. The video element is set to autoplay so the stream starts immediately.
<!-- If autoplay is omitted, the video stays on the first frame -->
<video id="video" autoplay></video>
const constraints = {
audio: false,
video: { width: 640, height: 480 }
};
navigator.mediaDevices.getUserMedia(constraints)
.then(stream => { video.srcObject = stream; })
.catch(err => { console.log(err); });Safari 11+ supports WebRTC; compatibility can be verified on caniuse.com.
Pixel processing
Capture a frame
Frames are drawn from the video element onto an off‑screen canvas for pixel‑level analysis.
const video = document.getElementById('video');
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 640;
canvas.height = 480;
function capture() {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
// further processing here
}Compute frame difference
The canvas compositing mode is set to difference. Drawing a second frame over the first produces an image where bright pixels represent pixel‑wise differences.
function diffTwoImage(firstImg, secondImg) {
ctx.globalCompositeOperation = 'difference';
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(firstImg, 0, 0);
ctx.drawImage(secondImg, 0, 0);
}Determine motion intensity
The resulting ImageData is scanned; the RGB components of each pixel are summed and compared against a configurable threshold. Pixels exceeding the threshold are counted. If the count surpasses a second threshold, motion is considered detected.
let imageScore = 0;
const rgba = imageData.data;
for (let i = 0; i < rgba.length; i += 4) {
const r = rgba[i] / 3;
const g = rgba[i + 1] / 3;
const b = rgba[i + 2] / 3;
const pixelScore = r + g + b;
if (pixelScore >= PIXEL_SCORE_THRESHOLD) {
imageScore++;
}
}
if (imageScore >= IMAGE_SCORE_THRESHOLD) {
// motion detected
}Track moving objects
Bright pixels are examined to compute the minimum and maximum X/Y coordinates, forming a bounding rectangle that encloses the motion area. The rectangle is drawn with ctx.strokeRect(), adding 0.5 to coordinates to avoid anti‑aliasing blur.
function processDiff(imageData) {
const rgba = imageData.data;
let motionBox = null;
for (let i = 0; i < rgba.length; i += 4) {
const pixelScore = (rgba[i] + rgba[i+1] + rgba[i+2]) / 3;
if (pixelScore >= 80) {
const coord = calcCoord(i);
motionBox = calcMotionBox(motionBox, coord.x, coord.y);
}
}
return motionBox;
}
function calcMotionBox(cur, x, y) {
const box = cur || { x: { min: x, max: x }, y: { min: y, max: y } };
box.x.min = Math.min(box.x.min, x);
box.x.max = Math.max(box.x.max, x);
box.y.min = Math.min(box.y.min, y);
box.y.max = Math.max(box.y.max, y);
return box;
}
function calcCoord(i) {
return { x: (i / 4) % diffWidth, y: Math.floor((i / 4) / diffWidth) };
}
// drawing the rectangle
ctx.lineWidth = 6;
ctx.strokeRect(
motionBox.x.min + 0.5,
motionBox.y.min + 0.5,
motionBox.x.max - motionBox.x.min,
motionBox.y.max - motionBox.y.min
);Performance considerations
Downscale the canvas
Processing a full‑size 640×480 canvas requires examining 307,200 pixels per frame. Reducing the off‑screen canvas dimensions by a factor of 10 (e.g., 64×48) cuts the pixel count to ~30,720, dramatically improving speed.
const motionCanvas = document.createElement('canvas');
const backgroundCanvas = document.createElement('canvas');
motionCanvas.width = 640;
motionCanvas.height = 48;
backgroundCanvas.width = 64; // 10× smaller
backgroundCanvas.height = 48;Timer interval
Instead of processing at 60 fps, compare frames at a configurable interval (e.g., every 100 ms). This is sufficient for simple motion detection while reducing CPU load.
Extensions
The same pixel‑difference technique can be applied to visual regression testing (comparing design mockups or browser renderings). For 3‑D or skeletal tracking, Web‑compatible Kinect libraries such as DepthJS, Node‑Kinect2, ZigFu, and Kinect‑HTML5 can provide richer data streams.
DepthJS – browser‑plugin interface for Kinect data.
Node‑Kinect2 – Node.js server exposing full skeletal data.
ZigFu – supports HTML5, Unity3D, Flash with a comprehensive API.
Kinect‑HTML5 – C# service delivering color, depth, and skeleton streams.
References
HTML5 Kinect game development articles
“Motion Detection with JavaScript” tutorials
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Aotu Lab
Aotu Lab, founded in October 2015, is a front-end engineering team serving multi-platform products. The articles in this public account are intended to share and discuss technology, reflecting only the personal views of Aotu Lab members and not the official stance of JD.com Technology.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
