this is my code file that contains code and 3D model I used.
For this project, I want to explore some 3D stuff with p5 and webGL, so I can’t use the P5 editor since I am importing an obj file.
I wan to import a 3D mesh that use face mesh to change its animation. The mesh itself is in a looping rotation, but the face mesh can change its rotation angle and direction. when I open my mouse, it changes color, scale, and if I open my mouse very big it will explode to particles.
documentation video:
Code in my file:
// Face-controlled 3D model visualization with mouth detection
let faceMesh;
let video;
let faces = [];
let objModel;
let rotationAngle = 0;
let targetScale = 2.5;
let currentScale = 2.5;
let strokeColor;
let targetColor;
let originalVertices = [];
let lastTargetScale = 2.5; // Track when target changes, not current
function preload() {
faceMesh = ml5.faceMesh({ maxFaces: 1, refineLandmarks: false, flipHorizontal: false });
objModel = loadModel('model.obj', true, modelLoaded);
}
function modelLoaded() {
if (objModel.vertices) {
originalVertices = objModel.vertices.map(v => createVector(v.x, v.y, v.z));
}
}
function setup() {
createCanvas(640, 480, WEBGL);
video = createCapture(VIDEO);
video.size(640, 480);
video.hide();
faceMesh.detectStart(video, gotFaces);
strokeColor = color(255);
targetColor = color(255);
}
function draw() {
background(0);
rotationAngle += 0.005;
let isMouthOpen = false;
let isMouthVeryOpen = false;
if (faces.length > 0) {
let face = faces[0];
let mouthHeight = dist(face.keypoints[13].x, face.keypoints[13].y,
face.keypoints[14].x, face.keypoints[14].y);
isMouthOpen = mouthHeight > 10;
isMouthVeryOpen = mouthHeight > 20;
// Scale animation
targetScale = isMouthOpen ? 10.0 : 2.5;
// Color animation - only when target CHANGES (not during lerp)
if (targetScale !== lastTargetScale) {
targetColor = color(random(100, 255), random(100, 255), random(100, 255));
lastTargetScale = targetScale;
}
currentScale = lerp(currentScale, targetScale, 0.05);
strokeColor = lerpColor(strokeColor, targetColor, 0.2);
// Draw model
let nose = face.keypoints[1];
let rotationY = map(nose.x, 0, width, -PI / 4, PI / 4);
let rotationX = map(nose.y, 0, height, -PI / 6, PI / 6);
push();
rotateX(PI);
rotateY(rotationAngle);
rotateY(rotationY);
rotateX(rotationX);
scale(currentScale);
if (isMouthVeryOpen && originalVertices.length > 0) {
drawRandomizedModel();
} else {
noFill();
stroke(strokeColor);
strokeWeight(1);
model(objModel);
}
pop();
}
}
function drawRandomizedModel() {
stroke(strokeColor);
strokeWeight(3);
beginShape(POINTS);
for (let v of originalVertices) {
vertex(v.x + random(-30, 30), v.y + random(-30, 30), v.z + random(-30, 30));
}
endShape();
}
function gotFaces(results) {
faces = results;
}
*code modified by Claude code.