Python Ghost Body Transform / index.html
Back to Folderpython_hideman/index.html
Download ZIP
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>doniahub</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@600;800;900&family=Montserrat:wght@500;700&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation/selfie_segmentation.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/hands/hands.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/drawing_utils/drawing_utils.js" crossorigin="anonymous"></script>
<style>*{box-sizing:border-box;margin:0;padding:0;user-select:none}html,body{width:100vw;height:100vh;overflow:hidden;background:transparent;margin:0;padding:0}video{display:none}canvas#outputCanvas{position:fixed;top:0;left:0;width:100vw;height:100vh;object-fit:cover;transform:scaleX(-1);z-index:1}.watermark{position:fixed;bottom:25px;right:35px;z-index:10;font-family:'Orbitron',sans-serif;font-weight:800;font-size:1.45rem;letter-spacing:3px;text-transform:uppercase;background:linear-gradient(135deg,#ffffff 0%,#00ffcc 60%,#ff007f 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;filter:drop-shadow(0 0 12px rgba(0,255,204,0.6));pointer-events:none;opacity:.88;transition:opacity .3s ease}.watermark span{font-family:'Montserrat',sans-serif;font-weight:500;font-size:.85rem;letter-spacing:4px;display:block;color:rgba(255,255,255,0.8);text-align:right;-webkit-text-fill-color:initial;text-shadow:0 0 10px rgba(0,255,204,0.4)}</style>
</head>
<body>
<video id="webcam" playsinline autoplay muted></video>
<canvas id="outputCanvas"></canvas>
<div class="watermark">
DoniaHub
</div>
<script>
const video = document.getElementById('webcam');
const canvas = document.getElementById('outputCanvas');
const ctx = canvas.getContext('2d');
const bgCanvas = document.createElement('canvas');
const bgCtx = bgCanvas.getContext('2d');
let bgCaptured = false;
let invisibleMode = false;
let lastToggleTime = 0;
const debounceDelay = 750;
let pinchStartTime = 0;
let pinchTriggered = false;
const PINCH_HOLD_DURATION = 120;
let cloakProgress = 0.0;
const DISSOLVE_SPEED = 0.085;
let latestHandResults = null;
const hands = new Hands({
locateFile: (file) => `https://cdn.jsdelivr.net/npm/@mediapipe/hands/${file}`,
});
hands.setOptions({
maxNumHands: 2,
modelComplexity: 1,
minDetectionConfidence: 0.65,
minTrackingConfidence: 0.65,
});
hands.onResults((results) => {
latestHandResults = results;
});
async function startCamera() {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: {
width: { ideal: 1920 },
height: { ideal: 1080 },
facingMode: 'user'
},
audio: false,
});
video.srcObject = stream;
video.onloadedmetadata = () => {
video.play();
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
bgCanvas.width = video.videoWidth;
bgCanvas.height = video.videoHeight;
const camera = new Camera(video, {
onFrame: async () => {
await hands.send({ image: video });
},
width: video.videoWidth,
height: video.videoHeight,
});
camera.start();
setTimeout(() => {
captureBackground();
}, 1200);
requestAnimationFrame(renderLoop);
};
} catch (err) {
console.error("Camera access error:", err);
}
}
function captureBackground() {
bgCtx.drawImage(video, 0, 0, bgCanvas.width, bgCanvas.height);
bgCaptured = true;
console.log("[doniahub] Background captured successfully.");
}
function checkPinchGesture(handResults, width, height, now) {
if (!handResults || !handResults.multiHandLandmarks || handResults.multiHandLandmarks.length === 0) {
pinchStartTime = 0;
pinchTriggered = false;
return { isPinching: false, pinchX: 0, pinchY: 0, progress: 0, readyToToggle: false };
}
for (const lm of handResults.multiHandLandmarks) {
const palmSize = Math.hypot((lm[0].x - lm[9].x) * width, (lm[0].y - lm[9].y) * height);
if (palmSize < 15) continue;
const dx = (lm[4].x - lm[8].x) * width;
const dy = (lm[4].y - lm[8].y) * height;
const tipDist = Math.hypot(dx, dy);
const relativeRatio = tipDist / palmSize;
const zDiff = Math.abs(lm[4].z - lm[8].z);
if (relativeRatio < 0.22 && zDiff < 0.08) {
const pinchX = (lm[4].x + lm[8].x) * 0.5 * width;
const pinchY = (lm[4].y + lm[8].y) * 0.5 * height;
if (pinchStartTime === 0) {
pinchStartTime = now;
}
const elapsed = now - pinchStartTime;
const progress = Math.min(1.0, elapsed / PINCH_HOLD_DURATION);
const readyToToggle = progress >= 1.0 && !pinchTriggered;
if (readyToToggle) {
pinchTriggered = true;
}
return { isPinching: true, pinchX, pinchY, progress, readyToToggle };
}
}
pinchStartTime = 0;
pinchTriggered = false;
return { isPinching: false, pinchX: 0, pinchY: 0, progress: 0, readyToToggle: false };
}
function drawHandSkeleton(ctx, handResults, width, height) {
if (!handResults || !handResults.multiHandLandmarks) return;
for (const landmarks of handResults.multiHandLandmarks) {
drawConnectors(ctx, landmarks, HAND_CONNECTIONS, {
color: '#ff007f',
lineWidth: 4,
});
for (const lm of landmarks) {
const x = lm.x * width;
const y = lm.y * height;
ctx.save();
ctx.beginPath();
ctx.arc(x, y, 5, 0, 2 * Math.PI);
ctx.fillStyle = '#00ffcc';
ctx.shadowColor = '#00ffcc';
ctx.shadowBlur = 12;
ctx.fill();
ctx.restore();
}
}
}
function renderLoop() {
const now = performance.now();
const w = canvas.width;
const h = canvas.height;
const pinchState = checkPinchGesture(latestHandResults, w, h, now);
if (pinchState.readyToToggle) {
if (now - lastToggleTime > debounceDelay) {
if (!bgCaptured) {
captureBackground();
}
invisibleMode = !invisibleMode;
lastToggleTime = now;
}
}
const targetProgress = invisibleMode ? 1.0 : 0.0;
cloakProgress += (targetProgress - cloakProgress) * DISSOLVE_SPEED;
if (Math.abs(targetProgress - cloakProgress) < 0.005) {
cloakProgress = targetProgress;
}
const easeAlpha = 0.5 - 0.5 * Math.cos(cloakProgress * Math.PI);
if (easeAlpha >= 0.998 && bgCaptured) {
ctx.clearRect(0, 0, w, h);
ctx.drawImage(bgCanvas, 0, 0, w, h);
} else if (easeAlpha <= 0.002) {
ctx.clearRect(0, 0, w, h);
ctx.drawImage(video, 0, 0, w, h);
} else if (bgCaptured) {
ctx.clearRect(0, 0, w, h);
ctx.drawImage(bgCanvas, 0, 0, w, h);
ctx.save();
ctx.globalAlpha = 1.0 - easeAlpha;
ctx.drawImage(video, 0, 0, w, h);
ctx.restore();
}
drawHandSkeleton(ctx, latestHandResults, w, h);
requestAnimationFrame(renderLoop);
}
window.addEventListener('keydown', (e) => {
if (e.key === 'b' || e.key === 'B') {
captureBackground();
} else if (e.key === 't' || e.key === 'T' || e.key === ' ') {
invisibleMode = !invisibleMode;
}
});
window.addEventListener('DOMContentLoaded', startCamera);
</script>
</body>
</html>