1/** 2 * Pose estimation in a Web Worker â same logic as direct mode (WASM for MoveNet). 3 * Loads TFJS + WASM backend and pose-detection from CDN; can run in parallel with main thread. 4 */ 5// Pinned to 4.22.0 (matches the bundled npm version) for stable CDN caching. 6const TFJS_VERSION = "4.22.0"; 7const POSE_DETECTION_VERSION = "2.1.3"; 8const WASM_CDN = `https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@${TFJS_VERSION}/dist/`; 9// Self-hosted MoveNet SinglePose Lightning â avoids the slow tfhub.dev download. 10const MOVENET_MODEL_URL = 11 "https://app.posetracker.com/scripts/tmp_model_to_remove.json"; 12 13// Single importScripts call â libs fetched in parallel instead of sequentially. 14self.importScripts( 15 `https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-core@${TFJS_VERSION}`, 16 `https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@${TFJS_VERSION}`, 17 `https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-converter@${TFJS_VERSION}`, 18 `https://cdn.jsdelivr.net/npm/@tensorflow-models/pose-detection@${POSE_DETECTION_VERSION}` 19); 20 21let detector = null; 22 23self.onmessage = async function (e) { 24 const msg = e.data; 25 26 if (msg.action === "init") { 27 try { 28 const useBlaze = msg.blazepose === true; 29 const useTfjsBlaze = 30 useBlaze && 31 (msg.blazeposeRuntime === "tfjs" || 32 msg.blazeposeRuntime === "tensorflow" || 33 msg.blazeposeRuntime === "tf"); 34 35 if (useBlaze) { 36 if (!useTfjsBlaze) { 37 self.importScripts( 38 "https://cdn.jsdelivr.net/npm/@mediapipe/pose" 39 ); 40 } 41 if (msg.isAndroid === true) { 42 tf.env().set("WEBGL_CPU_FORWARD", false); 43 tf.env().set("WEBGL_PACK", false); 44 tf.env().set("WEBGL_FORCE_F16_TEXTURES", false); 45 tf.env().set("WEBGL_RENDER_FLOAT32_ENABLED", false); 46 tf.env().set("WEBGL_FLUSH_THRESHOLD", 1.75); 47 tf.env().set("WEBGL_DISJOINT_QUERY_TIMER_EXTENSION_VERSION", 0); 48 } 49 await tf.setBackend("webgl"); 50 await tf.ready(); 51 if (tf.getBackend() !== "webgl") { 52 self.postMessage({ type: "no-webgl" }); 53 return; 54 } 55 detector = await poseDetection.createDetector( 56 poseDetection.SupportedModels.BlazePose, 57 useTfjsBlaze 58 ? { runtime: "tfjs", modelType: "lite", enableSmoothing: false } 59 : { 60 runtime: "mediapipe", 61 solutionPath: "https://cdn.jsdelivr.net/npm/@mediapipe/pose", 62 enableSmoothing: false, 63 } 64 ); 65 } else { 66 tf.wasm.setWasmPaths(WASM_CDN); 67 await tf.setBackend("wasm"); 68 await tf.ready(); 69 if (tf.getBackend() !== "wasm") { 70 self.postMessage({ type: "no-wasm" }); 71 return; 72 } 73 detector = await poseDetection.createDetector( 74 poseDetection.SupportedModels.MoveNet, 75 { 76 modelType: poseDetection.movenet.modelType.SINGLEPOSE_LIGHTNING, 77 enableSmoothing: false, 78 modelUrl: MOVENET_MODEL_URL, 79 } 80 ); 81 } 82 self.postMessage({ type: "init-done" }); 83 } catch (err) { 84 self.postMessage({ type: "init-fail", error: String(err && err.message) }); 85 } 86 return; 87 } 88 89 if (msg.action === "estimate") { 90 if (!detector) return; 91 const imageBitmap = msg.image; 92 try { 93 const poses = await detector.estimatePoses(imageBitmap); 94 const keypoints = (poses[0] && poses[0].keypoints) || []; 95 self.postMessage({ 96 type: "pose", 97 keypoints: keypoints.map((kp) => ({ 98 name: kp.name, 99 x: kp.x, 100 y: kp.y, 101 z: kp.z != null ? kp.z : 0, 102 score: kp.score != null ? kp.score : 0, 103 })), 104 }); 105 } catch (err) { 106 self.postMessage({ type: "pose-error", error: String(err && err.message) }); 107 } finally { 108 if (imageBitmap && imageBitmap.close) imageBitmap.close(); 109 } 110 return; 111 } 112 113 if (msg.action === "dispose") { 114 if (detector && detector.dispose) detector.dispose(); 115 detector = null; 116 } 117};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.