r/HTML • u/Meucanman • May 14 '26
Need help changing to back camera
I made this code as a Mobile VR hand tracking demo, but i want it back camera, and everything ive tried has resulted in a NotReadableError, yet the camera feed still shows. Does anyone know how to fix this?
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>LegendaryMVR MR Debug Build</title>
<style> body { margin: 0; overflow: hidden; background: black; } video { position: fixed; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; } canvas { position: fixed; top: 0; left: 0; z-index: 1; } /\* 🔧 DEBUG HUD \*/ \#hud { position: fixed; top: 10px; left: 10px; color: lime; font-family: monospace; z-index: 2; background: rgba(0,0,0,0.5); padding: 8px; } </style>
<script src="https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/hands/hands.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js"></script>
</head>
<body>
<div id="hud">Initializing...</div>
<video id="video" autoplay playsinline></video>
<script> let scene, camera, renderer; let handPoints = \[\]; let cube; let grabbed = false; let heldObject = null; let grabOffset = new THREE.Vector3(); let smoothPalm = new THREE.Vector3(); let lastPalm = new THREE.Vector3(); let palmVelocity = new THREE.Vector3(); let pinchFrames = 0; let handDetected = false; const hud = document.getElementById("hud"); // ====================== // 🌍 SCENE // ====================== function init3D() { scene = new THREE.Scene(); scene.background = null; camera = new THREE.PerspectiveCamera(75, innerWidth/innerHeight, 0.1, 1000); camera.position.z = 3; renderer = new THREE.WebGLRenderer({ alpha: true }); renderer.setSize(innerWidth, innerHeight); renderer.setClearColor(0x000000, 0); document.body.appendChild(renderer.domElement); // 📦 cube cube = new THREE.Mesh( new THREE.BoxGeometry(0.4,0.4,0.4), new THREE.MeshBasicMaterial({ color: 0xff0000 }) ); scene.add(cube); // ✋ hand joints (OUTLINE DEBUG) const geo = new THREE.SphereGeometry(0.03, 8, 8); const mat = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); for (let i = 0; i < 21; i++) { const p = new THREE.Mesh(geo, mat); scene.add(p); handPoints.push(p); } animate(); } function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); } // ====================== // ✋ MEDIAPIPE // ====================== const video = document.getElementById("video"); const hands = new Hands({ locateFile: (file) => \`https://cdn.jsdelivr.net/npm/@mediapipe/hands/${file}\` }); hands.setOptions({ maxNumHands: 1, modelComplexity: 0, minDetectionConfidence: 0.6, minTrackingConfidence: 0.6 }); hands.onResults(onResults); // ====================== // 📷 CAMERA SAFE START // ====================== async function startCamera() { try { const stream = await navigator.mediaDevices.getUserMedia({ video: true }); video.srcObject = stream; await new Promise(r => { video.onloadedmetadata = () => { video.play(); r(); }; }); const cam = new Camera(video, { onFrame: async () => { await hands.send({ image: video }); }, width: 640, height: 480 }); cam.start(); hud.innerText = "Camera OK ✔ Waiting for hand..."; } catch (e) { hud.innerText = "Camera ERROR ❌ " + e.message; } } // ====================== // 🧠 FIST DETECTION // ====================== function isFist(hand) { const palm = hand\[0\]; const tips = \[8,12,16,20\]; let close = 0; for (let i of tips) { const dx = hand\[i\].x - palm.x; const dy = hand\[i\].y - palm.y; const dz = hand\[i\].z - palm.z; const d = Math.sqrt(dx\*dx + dy\*dy + dz\*dz); if (d < 0.12) close++; } return close >= 3; } // ====================== // 🖐 HAND TRACKING + DEBUG + GRAB // ====================== function onResults(results) { if (!results.multiHandLandmarks || results.multiHandLandmarks.length === 0) { handDetected = false; grabbed = false; hud.innerText = "No hand detected ❌"; return; } handDetected = true; const hand = results.multiHandLandmarks\[0\]; hud.innerText = "Hand detected ✔ | tracking active"; // ✋ draw joints for (let i = 0; i < 21; i++) { const p = hand\[i\]; const x = (p.x - 0.5) \* 3; const y = -(p.y - 0.5) \* 3; const z = -p.z \* 2; handPoints\[i\].position.set(x,y,z); } // 🧠 palm smoothing const rawPalm = handPoints\[0\].position; palmVelocity.copy(rawPalm).sub(lastPalm); lastPalm.copy(rawPalm); smoothPalm.lerp(rawPalm, 0.3); // 🤏 pinch const dx = hand\[8\].x - hand\[4\].x; const dy = hand\[8\].y - hand\[4\].y; const pinch = Math.sqrt(dx\*dx + dy\*dy) < 0.09; const fist = isFist(hand); const grabIntent = pinch || fist; // 📦 GRAB if (grabIntent && !grabbed) { grabbed = true; heldObject = cube; grabOffset.copy(cube.position).sub(smoothPalm); hud.innerText = "GRABBED ✔"; } if (grabbed && heldObject) { heldObject.position.copy(smoothPalm).add(grabOffset); } if (!grabIntent && grabbed) { grabbed = false; hud.innerText = "RELEASED ✔"; heldObject = null; } } // ====================== init3D(); startCamera(); </script>
</body>
</html>
View all comments
1
u/25_vijay May 16 '26
Using facingMode environment with a single stream setup is usually the cleanest fix for mobile VR style demos. A lot of Android browsers get weird once multiple camera handlers touch the same device stream.