Files
webments/websnap/script/main.js
2026-03-14 17:58:18 +01:00

106 lines
3.1 KiB
JavaScript

/* main.js - websnap */
import { HandLandmarker, FilesetResolver }
from "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest";
/* set html elements */
const video = document.getElementById("video");
const canvas = document.getElementById("canvas");
const overlay = document.getElementById("overlay");
const octx = overlay.getContext("2d");
const ctx = canvas.getContext("2d");
const flash = document.getElementById("flash");
let lastShot = 0;
/* function to request fullscreen */
function requestFS() {
const el = document.documentElement;
(el.requestFullscreen || el.webkitRequestFullscreen || el.mozRequestFullScreen)
?.call(el);
}
requestFS();
/* add listeners for fullscreen */
document.addEventListener("click", requestFS, { once: true });
document.addEventListener("touchend", requestFS, { once: true });
document.addEventListener("dblclick", requestFS);
/* camera setup */
const stream = await navigator.mediaDevices.getUserMedia({
/* ideal res: 4096x2160 ; 4k */
video: { width: { ideal: 4096 }, height: { ideal: 2160 }, facingMode: "user" }
});
video.srcObject = stream;
/* import mediapipe for handgestures */
const vision = await FilesetResolver.forVisionTasks(
"https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision/wasm"
);
const handLandmarker = await HandLandmarker.createFromOptions(vision, {
baseOptions: {
modelAssetPath:
"https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/1/hand_landmarker.task"
},
runningMode: "VIDEO",
numHands: 1
});
/* flash for when picture gets taken */
function triggerFlash() {
flash.classList.add("pop");
requestAnimationFrame(() => requestAnimationFrame(() => flash.classList.remove("pop")));
}
/* capture a picture */
function takePhoto() {
triggerFlash();
const w = video.videoWidth, h = video.videoHeight;
canvas.width = w; canvas.height = h;
ctx.drawImage(video, 0, 0, w, h);
canvas.toBlob(blob => {
if (!blob) return;
saveAs(blob, "websnap-" + Date.now() + ".png");
}, "image/png");
}
/* detect shit */
function detectLoop(timestamp) {
const dw = overlay.offsetWidth, dh = overlay.offsetHeight;
if (overlay.width !== dw || overlay.height !== dh) {
overlay.width = dw; overlay.height = dh;
}
/* draw rect */
octx.clearRect(0, 0, dw, dh);
if (video.readyState < 2) { requestAnimationFrame(detectLoop); return; }
const result = handLandmarker.detectForVideo(video, timestamp);
if (result.landmarks.length) {
const lm = result.landmarks[0];
/* finger indexes, for peace sign */
const indexUp = lm[8].y < lm[6].y;
const middleUp = lm[12].y < lm[10].y;
const ringDown = lm[16].y > lm[14].y;
const pinkyDown = lm[20].y > lm[18].y;
const fingerGap = Math.abs(lm[8].x - lm[12].x);
const peace =
indexUp &&
middleUp &&
ringDown &&
pinkyDown &&
fingerGap > 0.05;
if (peace && Date.now() - lastShot > 2500) { /* wait a bit */
takePhoto();
lastShot = Date.now();
}
}
requestAnimationFrame(detectLoop);
}
requestAnimationFrame(detectLoop);