/* 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);