Uses ml5.js to perform real-time tracking of 468 face landmarks and draw a face mesh.
p5.js 2.0 ml5.js 1.0
Camera ml5.js Face Mesh
View Source Code
let video;
let faceMesh;
let faces = [];
let options = { maxFaces: 1, refineLandmarks: false, flipHorizontal: false };
let isModelStarted = false;
let isModelLoaded = false;
let pointSizeInput, pointSizeVal;
function setup() {
createCanvas(windowWidth, windowHeight);
pixelDensity(1);
// Start capture without awaiting it in setup
video = createCapture(VIDEO);
video.size(640, 480);
video.hide();
// Load ml5 model asynchronously
ml5.faceMesh(options).then((results) => {
faceMesh = results;
isModelLoaded = true;
});
pointSizeInput = select("#pointSize");
pointSizeVal = select("#pointSizeVal");
}
function gotFaces(results) {
faces = results;
}
function draw() {
background(20);
// Safeguard: Ensure video and model are fully ready
if (
!isModelLoaded ||
!video ||
!video.elt ||
video.elt.readyState < 2 ||
video.elt.videoWidth === 0
) {
fill(255);
textAlign(CENTER, CENTER);
textSize(24);
text("Initializing AI Model... Please wait", width / 2, height / 2);
return;
}
// Start ml5 detection
if (!isModelStarted) {
faceMesh.detectStart(video.elt, gotFaces);
isModelStarted = true;
}
let pSize = 2;
if (pointSizeInput) {
pSize = int(pointSizeInput.value());
if (pointSizeVal) pointSizeVal.html(pSize);
}
// Draw video (mirrored)
push();
translate(width, 0);
scale(-1, 1);
// Use video.width / video.height if it's a p5.Element,
// or video.elt.videoWidth if it's the raw element.
// In p5.js 2.0, video should be a p5.Element
let vW = video.width || video.elt.videoWidth;
let vH = video.height || video.elt.videoHeight;
let aspect = vW / vH;
let drawW = height * aspect;
let drawH = height;
if (drawW > width) {
drawW = width;
drawH = width / aspect;
}
let drawX = (width - drawW) / 2;
let drawY = (height - drawH) / 2;
tint(255, 100);
// Pass the video object itself. If still failing, p5 2.0 might have a bug or different API.
image(video, drawX, drawY, drawW, drawH);
noTint();
// Draw points
fill(0, 255, 255);
noStroke();
if (faces && faces.length > 0) {
for (let i = 0; i < faces.length; i++) {
let face = faces[i];
if (face && face.keypoints) {
for (let j = 0; j < face.keypoints.length; j++) {
let keypoint = face.keypoints[j];
let x = map(keypoint.x, 0, vW, drawX, drawX + drawW);
let y = map(keypoint.y, 0, vH, drawY, drawY + drawH);
circle(x, y, pSize);
}
}
}
}
pop();
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
}