{"_id":"@alternitis-corp/liveness","_rev":"7-eec162d01266f7447882aa6cf335b8d3","name":"@alternitis-corp/liveness","dist-tags":{"latest":"0.6.1"},"versions":{"0.1.0":{"name":"@alternitis-corp/liveness","version":"0.1.0","keywords":["liveness","liveness-detection","face","selfie","kyc","kyb","anti-spoofing","mediapipe","face-landmarker","head-turn","camera","webcam"],"author":{"name":"Alternitis Corp"},"license":"MIT","_id":"@alternitis-corp/liveness@0.1.0","maintainers":[{"name":"alternitis","email":"rxarinze@live.com"}],"homepage":"https://github.com/dragonalter/liveness#readme","bugs":{"url":"https://github.com/dragonalter/liveness/issues"},"dist":{"shasum":"eb701da75bb34d6ed361ee652f5a76260a22c961","tarball":"https://registry.npmjs.org/@alternitis-corp/liveness/-/liveness-0.1.0.tgz","fileCount":18,"integrity":"sha512-iua/phIpQaV8e2uZLgckz+a31fS5pR2FVWeJEtYiej8RL1X/txPJy8Q+saYsCxJdq3s90Yu/o1abJoDMRc5PBA==","signatures":[{"sig":"MEQCIG6VTvq+6sqE6lSoC3FM+imnyfuqD+wwrICEqRsQ7fiYAiAMvqZhtokhODi8Mm3Is3LFH45/H/G7/oUo50zbXkyuMg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":54494},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","source":"./src/index.ts","default":"./dist/index.js"},"./react":{"types":"./dist/react.d.ts","source":"./src/react.tsx","default":"./dist/react.js"},"./package.json":"./package.json"},"gitHead":"31d55a16d2617b443e4d7ff96846bcb9051bb693","scripts":{"build":"tsc -p tsconfig.json","clean":"rm -rf dist","typecheck":"tsc -p tsconfig.json --noEmit","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"alternitis","email":"rxarinze@live.com"},"repository":{"url":"git+https://github.com/dragonalter/liveness.git","type":"git"},"_npmVersion":"10.9.2","description":"Embedded, browser-based liveness check — Face-ID-style head-turn detection via MediaPipe FaceLandmarker. Framework-agnostic core + optional React wrapper.","directories":{},"sideEffects":false,"_nodeVersion":"22.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19","typescript":"^5","@types/react":"^19"},"peerDependencies":{"react":">=17"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/liveness_0.1.0_1782132870178_0.04652958113254879","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@alternitis-corp/liveness","version":"0.2.0","keywords":["liveness","liveness-detection","face","selfie","kyc","kyb","anti-spoofing","mediapipe","face-landmarker","head-turn","camera","webcam"],"author":{"name":"Alternitis Corp"},"license":"MIT","_id":"@alternitis-corp/liveness@0.2.0","maintainers":[{"name":"alternitis","email":"rxarinze@live.com"}],"homepage":"https://github.com/dragonalter/liveness#readme","bugs":{"url":"https://github.com/dragonalter/liveness/issues"},"dist":{"shasum":"8e2932edd32a879874d17bd9a900d8493eb12106","tarball":"https://registry.npmjs.org/@alternitis-corp/liveness/-/liveness-0.2.0.tgz","fileCount":18,"integrity":"sha512-s0EQ5b3s28No5udcNPSMRw5NLTJkgcJ/Iuru2ZOrQ41MoxTjyCdRIrS4RLU/RH11jeEf2vzSJN+CVB63z7HmjQ==","signatures":[{"sig":"MEUCIQCV/UUjuUXVAfDVq33E4iMvGNFSrGjnAjlRc/uDS1OzKQIgBhbnPWTpjVVV5oCAhm3KVlrro2XXrm3RZF9POH2YWyI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":57666},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","source":"./src/index.ts","default":"./dist/index.js"},"./react":{"types":"./dist/react.d.ts","source":"./src/react.tsx","default":"./dist/react.js"},"./package.json":"./package.json"},"gitHead":"ab8a610c716504f091d83b22f6c1c44b332aba59","scripts":{"build":"tsc -p tsconfig.json","clean":"rm -rf dist","typecheck":"tsc -p tsconfig.json --noEmit","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"alternitis","email":"rxarinze@live.com"},"repository":{"url":"git+https://github.com/dragonalter/liveness.git","type":"git"},"_npmVersion":"10.9.2","description":"Embedded, browser-based liveness check — Face-ID-style head-turn detection via MediaPipe FaceLandmarker. Framework-agnostic core + optional React wrapper.","directories":{},"sideEffects":false,"_nodeVersion":"22.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"^19","typescript":"^5","@types/react":"^19"},"peerDependencies":{"react":">=17"},"peerDependenciesMeta":{"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/liveness_0.2.0_1782144514126_0.27241865085868233","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@alternitis-corp/liveness","version":"0.3.0","keywords":["liveness","liveness-detection","face","selfie","kyc","kyb","anti-spoofing","mediapipe","face-landmarker","head-turn","camera","webcam","react","vue"],"author":{"name":"Alternitis Corp"},"license":"MIT","_id":"@alternitis-corp/liveness@0.3.0","maintainers":[{"name":"alternitis","email":"rxarinze@live.com"}],"homepage":"https://github.com/dragonalter/liveness#readme","bugs":{"url":"https://github.com/dragonalter/liveness/issues"},"dist":{"shasum":"ec48e64584b209aa7c3a8d536aaf9e7e836d1604","tarball":"https://registry.npmjs.org/@alternitis-corp/liveness/-/liveness-0.3.0.tgz","fileCount":24,"integrity":"sha512-PNQ5Bkyw0a1909BFGUGKMl4QdA06kyH/Kgw9LuCtZNINYzcp6mtA/gZyeZQb2lLE2Q5A9HOq9lSovefxyUXK7A==","signatures":[{"sig":"MEYCIQDMrlBbB6T5VxsPjZzo+wfEFdCckuX95MB/feR/zZ0vPQIhAJNroqZP0Cq2kW7ZP3203bHcSD9glzrWdBvgZiM+O479","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":81595},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","unpkg":"./dist/liveness.global.js","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","source":"./src/index.ts","default":"./dist/index.js"},"./vue":{"types":"./dist/vue.d.ts","source":"./src/vue.ts","default":"./dist/vue.js"},"./react":{"types":"./dist/react.d.ts","source":"./src/react.tsx","default":"./dist/react.js"},"./global":"./dist/liveness.global.js","./package.json":"./package.json"},"gitHead":"8bebeb39b43e5c186743c5a150eafdf74a58d388","scripts":{"build":"tsc -p tsconfig.json && npm run build:global","clean":"rm -rf dist","typecheck":"tsc -p tsconfig.json --noEmit","build:global":"esbuild dist/index.js --bundle --format=iife --global-name=Liveness --outfile=dist/liveness.global.js --minify","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"alternitis","email":"rxarinze@live.com"},"jsdelivr":"./dist/liveness.global.js","repository":{"url":"git+https://github.com/dragonalter/liveness.git","type":"git"},"_npmVersion":"10.9.2","description":"Embedded, browser-based liveness check — Face-ID-style guided head-turn via MediaPipe FaceLandmarker. Framework-agnostic core + React & Vue wrappers + a script-tag global build.","directories":{},"sideEffects":false,"_nodeVersion":"22.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vue":"^3.4","react":"^19","esbuild":"^0.24","typescript":"^5","@types/react":"^19"},"peerDependencies":{"vue":">=3","react":">=17"},"peerDependenciesMeta":{"vue":{"optional":true},"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/liveness_0.3.0_1782147109995_0.9701635129151256","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@alternitis-corp/liveness","version":"0.4.0","keywords":["liveness","liveness-detection","face","selfie","kyc","kyb","anti-spoofing","mediapipe","face-landmarker","head-turn","camera","webcam","react","vue"],"author":{"name":"Alternitis Corp"},"license":"MIT","_id":"@alternitis-corp/liveness@0.4.0","maintainers":[{"name":"alternitis","email":"rxarinze@live.com"}],"homepage":"https://github.com/dragonalter/liveness#readme","bugs":{"url":"https://github.com/dragonalter/liveness/issues"},"dist":{"shasum":"4769b9ce64c1d07a5e52090b7a4bd4d6717a16bb","tarball":"https://registry.npmjs.org/@alternitis-corp/liveness/-/liveness-0.4.0.tgz","fileCount":24,"integrity":"sha512-JaNDDFrTmthQ4SiIX5UZ+6fMCnodsWPqAJdSqR8zO34fV1hEvEDWXvIOMDf/GzqZehdZmWe5A3R7fxWPK/F8zg==","signatures":[{"sig":"MEUCIEUzmyhOo8ylkrw+hyKGMIyixYJxbUbQoMzsKbEiS7ZCAiEA+eokKNGdkVuGqv5tkiPtGcJRPwcFWfDrmOs0cCiNiZI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":84506},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","unpkg":"./dist/liveness.global.js","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","source":"./src/index.ts","default":"./dist/index.js"},"./vue":{"types":"./dist/vue.d.ts","source":"./src/vue.ts","default":"./dist/vue.js"},"./react":{"types":"./dist/react.d.ts","source":"./src/react.tsx","default":"./dist/react.js"},"./global":"./dist/liveness.global.js","./package.json":"./package.json"},"gitHead":"0a4a50a261f4431d377ac893298c693402701446","scripts":{"build":"tsc -p tsconfig.json && npm run build:global","clean":"rm -rf dist","typecheck":"tsc -p tsconfig.json --noEmit","build:global":"esbuild dist/index.js --bundle --format=iife --global-name=Liveness --outfile=dist/liveness.global.js --minify","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"alternitis","email":"rxarinze@live.com"},"jsdelivr":"./dist/liveness.global.js","repository":{"url":"git+https://github.com/dragonalter/liveness.git","type":"git"},"_npmVersion":"10.9.2","description":"Embedded, browser-based liveness check — Face-ID-style guided head-turn via MediaPipe FaceLandmarker. Framework-agnostic core + React & Vue wrappers + a script-tag global build.","directories":{},"sideEffects":false,"_nodeVersion":"22.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vue":"^3.4","react":"^19","esbuild":"^0.24","typescript":"^5","@types/react":"^19"},"peerDependencies":{"vue":">=3","react":">=17"},"peerDependenciesMeta":{"vue":{"optional":true},"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/liveness_0.4.0_1782150022331_0.665281654286074","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@alternitis-corp/liveness","version":"0.5.0","keywords":["liveness","liveness-detection","face","selfie","kyc","kyb","anti-spoofing","mediapipe","face-landmarker","head-turn","camera","webcam","react","vue"],"author":{"name":"Alternitis Corp"},"license":"MIT","_id":"@alternitis-corp/liveness@0.5.0","maintainers":[{"name":"alternitis","email":"rxarinze@live.com"}],"homepage":"https://github.com/dragonalter/liveness#readme","bugs":{"url":"https://github.com/dragonalter/liveness/issues"},"dist":{"shasum":"29cbdc5492215a25ec8d85c3a386a80d5d0a46bf","tarball":"https://registry.npmjs.org/@alternitis-corp/liveness/-/liveness-0.5.0.tgz","fileCount":24,"integrity":"sha512-1oc7+FQCvp+5UB8d0JHpvZCVoZvGDyQcdVNSHuja/A35mN1yJ4DMd54rJpF8MXKbfvsO1WLH7lz8K/u1VxjD1g==","signatures":[{"sig":"MEYCIQCZApaH57APPzSKHBcVkpGsG7BWppkFAPaBqFKzf5moVgIhAIa+b5qdCmHe/hyGpcGcdVLm7GAAwhAHCDiDJsaaq4Ea","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":109549},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","unpkg":"./dist/liveness.global.js","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","source":"./src/index.ts","default":"./dist/index.js"},"./vue":{"types":"./dist/vue.d.ts","source":"./src/vue.ts","default":"./dist/vue.js"},"./react":{"types":"./dist/react.d.ts","source":"./src/react.tsx","default":"./dist/react.js"},"./global":"./dist/liveness.global.js","./package.json":"./package.json"},"gitHead":"74572d5932dc5e2acfaae57fc9bd488e51c07cd2","scripts":{"build":"tsc -p tsconfig.json && npm run build:global","clean":"rm -rf dist","typecheck":"tsc -p tsconfig.json --noEmit","build:global":"esbuild dist/index.js --bundle --format=iife --global-name=Liveness --outfile=dist/liveness.global.js --minify","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"alternitis","email":"rxarinze@live.com"},"jsdelivr":"./dist/liveness.global.js","repository":{"url":"git+https://github.com/dragonalter/liveness.git","type":"git"},"_npmVersion":"10.9.2","description":"Embedded, browser-based liveness check — Face-ID-style guided head-turn via MediaPipe FaceLandmarker. Framework-agnostic core + React & Vue wrappers + a script-tag global build.","directories":{},"sideEffects":false,"_nodeVersion":"22.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vue":"^3.4","react":"^19","esbuild":"^0.24","typescript":"^5","@types/react":"^19"},"peerDependencies":{"vue":">=3","react":">=17"},"peerDependenciesMeta":{"vue":{"optional":true},"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/liveness_0.5.0_1782152179408_0.7786524862290516","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"@alternitis-corp/liveness","version":"0.6.0","keywords":["liveness","liveness-detection","face","selfie","kyc","kyb","anti-spoofing","mediapipe","face-landmarker","head-turn","camera","webcam","react","vue"],"author":{"name":"Alternitis Corp"},"license":"MIT","_id":"@alternitis-corp/liveness@0.6.0","maintainers":[{"name":"alternitis","email":"rxarinze@live.com"}],"homepage":"https://github.com/dragonalter/liveness#readme","bugs":{"url":"https://github.com/dragonalter/liveness/issues"},"dist":{"shasum":"da1a46ad9782da9993e2bf989998faa6f1d6508b","tarball":"https://registry.npmjs.org/@alternitis-corp/liveness/-/liveness-0.6.0.tgz","fileCount":24,"integrity":"sha512-DrcqvJeg3EQ+RtlFtKY5vrK54FVdMTQaGFKX+FdWR/jkVN94O/EISWWWVf5IVV2I9cbtcxz4/VYDGKdoAh+/yw==","signatures":[{"sig":"MEYCIQDJhoAQQPXoZui22W8WYbKFGxWhT2w4c3ujDpRgrRrWpgIhAMOFX/6rHDEHYlPaEvUVmkMmZ/PKlTGhzp41ovSw2quR","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":114319},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","unpkg":"./dist/liveness.global.js","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","source":"./src/index.ts","default":"./dist/index.js"},"./vue":{"types":"./dist/vue.d.ts","source":"./src/vue.ts","default":"./dist/vue.js"},"./react":{"types":"./dist/react.d.ts","source":"./src/react.tsx","default":"./dist/react.js"},"./global":"./dist/liveness.global.js","./package.json":"./package.json"},"gitHead":"41516e48058423a9cdc650ed8874913613b1ec3d","scripts":{"build":"tsc -p tsconfig.json && npm run build:global","clean":"rm -rf dist","typecheck":"tsc -p tsconfig.json --noEmit","build:global":"esbuild dist/index.js --bundle --format=iife --global-name=Liveness --outfile=dist/liveness.global.js --minify","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"alternitis","email":"rxarinze@live.com"},"jsdelivr":"./dist/liveness.global.js","repository":{"url":"git+https://github.com/dragonalter/liveness.git","type":"git"},"_npmVersion":"10.9.2","description":"Embedded, browser-based liveness check — Face-ID-style guided head-turn via MediaPipe FaceLandmarker. Framework-agnostic core + React & Vue wrappers + a script-tag global build.","directories":{},"sideEffects":false,"_nodeVersion":"22.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vue":"^3.4","react":"^19","esbuild":"^0.24","typescript":"^5","@types/react":"^19"},"peerDependencies":{"vue":">=3","react":">=17"},"peerDependenciesMeta":{"vue":{"optional":true},"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/liveness_0.6.0_1782214570113_0.8499224568013537","host":"s3://npm-registry-packages-npm-production"}},"0.6.1":{"name":"@alternitis-corp/liveness","version":"0.6.1","description":"Embedded, browser-based liveness check — Face-ID-style guided head-turn via MediaPipe FaceLandmarker. Framework-agnostic core + React & Vue wrappers + a script-tag global build.","license":"MIT","author":{"name":"Alternitis Corp"},"type":"module","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","unpkg":"./dist/liveness.global.js","jsdelivr":"./dist/liveness.global.js","exports":{".":{"source":"./src/index.ts","types":"./dist/index.d.ts","default":"./dist/index.js"},"./react":{"source":"./src/react.tsx","types":"./dist/react.d.ts","default":"./dist/react.js"},"./vue":{"source":"./src/vue.ts","types":"./dist/vue.d.ts","default":"./dist/vue.js"},"./global":"./dist/liveness.global.js","./package.json":"./package.json"},"sideEffects":false,"scripts":{"build":"tsc -p tsconfig.json && npm run build:global","build:global":"esbuild dist/index.js --bundle --format=iife --global-name=Liveness --outfile=dist/liveness.global.js --minify","clean":"rm -rf dist","typecheck":"tsc -p tsconfig.json --noEmit","prepublishOnly":"npm run clean && npm run build"},"peerDependencies":{"react":">=17","vue":">=3"},"peerDependenciesMeta":{"react":{"optional":true},"vue":{"optional":true}},"devDependencies":{"@types/react":"^19","esbuild":"^0.24","react":"^19","typescript":"^5","vue":"^3.4"},"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/dragonalter/liveness.git"},"homepage":"https://github.com/dragonalter/liveness#readme","bugs":{"url":"https://github.com/dragonalter/liveness/issues"},"keywords":["liveness","liveness-detection","face","selfie","kyc","kyb","anti-spoofing","mediapipe","face-landmarker","head-turn","camera","webcam","react","vue"],"_id":"@alternitis-corp/liveness@0.6.1","gitHead":"98c2c3d5b767909cf40f686a614391ee70ef9e0e","_nodeVersion":"22.14.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-FivIR1NWjV8x+BltUOl0yQxxWnXQYfraEpqVNEEy8Qhy9gj2ESrBFXXbCepGDWpTC+ha1lmaH/NnB05BEByi/Q==","shasum":"31b3583c42fa18477b3d6db3c7587f88c9627313","tarball":"https://registry.npmjs.org/@alternitis-corp/liveness/-/liveness-0.6.1.tgz","fileCount":24,"unpackedSize":115078,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCDh95H8KWuGr3UmQ60Qv4G2E8h4YFLfazjBh8mu4YHqwIgRzqcVTjw70hBzQlLFHmf3TRh8SLuSIe61krbSyaPtnc="}]},"_npmUser":{"name":"alternitis","email":"rxarinze@live.com"},"directories":{},"maintainers":[{"name":"alternitis","email":"rxarinze@live.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/liveness_0.6.1_1782223148074_0.7810522192735503"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-22T12:54:30.021Z","modified":"2026-06-23T13:59:08.415Z","0.1.0":"2026-06-22T12:54:30.332Z","0.2.0":"2026-06-22T16:08:34.308Z","0.3.0":"2026-06-22T16:51:50.160Z","0.4.0":"2026-06-22T17:40:22.482Z","0.5.0":"2026-06-22T18:16:19.579Z","0.6.0":"2026-06-23T11:36:10.248Z","0.6.1":"2026-06-23T13:59:08.239Z"},"bugs":{"url":"https://github.com/dragonalter/liveness/issues"},"author":{"name":"Alternitis Corp"},"license":"MIT","homepage":"https://github.com/dragonalter/liveness#readme","keywords":["liveness","liveness-detection","face","selfie","kyc","kyb","anti-spoofing","mediapipe","face-landmarker","head-turn","camera","webcam","react","vue"],"repository":{"type":"git","url":"git+https://github.com/dragonalter/liveness.git"},"description":"Embedded, browser-based liveness check — Face-ID-style guided head-turn via MediaPipe FaceLandmarker. Framework-agnostic core + React & Vue wrappers + a script-tag global build.","maintainers":[{"name":"alternitis","email":"rxarinze@live.com"}],"readme":"# @alternitis-corp/liveness\n\nEmbedded, **browser-based ACTIVE liveness check** with a Face‑ID‑style scan ring. It guides the user\nthrough an **ordered, one‑at‑a‑time** sequence — **turn left → turn right → look up → look down →\nblink → open mouth** — where each head step lights only its own **arc of the ring** (left arc turning\nleft, top arc looking up, …), captures the face from multiple angles, and hands you the frames.\n\nYaw (left/right) and pitch (up/down) are measured from translation‑invariant face landmarks (via Google\n**MediaPipe FaceLandmarker**), so panning or shaking the device does **not** advance it — only a real\nhead turn does. Blink and mouth‑open come from MediaPipe **face blendshapes** (`eyeBlinkLeft/Right`,\n`jawOpen`), with a landmark EAR/MAR fallback. A photo, a static dummy head, or a naïve replay can’t\nsatisfy the active challenges. Deliberate, smooth, and yields a detailed multi‑angle set you still\nverify server‑side.\n\n- 🛡️ **Active liveness** — ordered turn / look‑up‑down / blink / mouth challenges, each lighting its own ring arc, defeat photos & replays\n- 🎯 **Framework‑agnostic core** (plain DOM) — works in React, Vue, Svelte, Angular, vanilla, Expo Web…\n- ⚛️ **First‑class wrappers** for React and Vue, plus a **`<script>` global build** (no bundler needed)\n- 📦 **Zero runtime dependencies** — the MediaPipe model + WASM load lazily from a CDN (overridable)\n- 🔒 Runs entirely client‑side; you decide what to do with the captured frames\n- 🪶 Renders its own live video + animated ring; you supply the surrounding UI and instruction text\n\n---\n\n## Install\n\n```bash\nnpm install @alternitis-corp/liveness\n```\n\n> **Requires a secure context.** `getUserMedia` only works over **HTTPS** (or `localhost`). On first\n> use the browser downloads the MediaPipe face model (~13 MB) and caches it. Feature‑detect with\n> `isLivenessSupported()`.\n\n---\n\n## Usage\n\n### React\n\n```tsx\nimport { LivenessCamera } from '@alternitis-corp/liveness/react';\n\nexport function Step() {\n  return (\n    <LivenessCamera\n      size={260}\n      turnThreshold={0.16}                       // higher = the user must turn further\n      onPrompt={(p) => setHint(promptText[p])}   // 'center' | 'turn-left' | 'turn-right' | 'hold' | 'complete'\n      onProgress={(p) => {}}                      // 0..1 ring fill\n      onComplete={(frames) => upload(frames)}     // JPEG data URLs across the head angles\n      onError={(e) => alert(e.message)}\n    />\n  );\n}\n```\n\n### Vue 3\n\n```vue\n<script setup lang=\"ts\">\nimport { LivenessCamera } from '@alternitis-corp/liveness/vue';\nfunction onFrames(frames: string[]) { upload(frames); }\n</script>\n\n<template>\n  <LivenessCamera :turn-threshold=\"0.16\" @prompt=\"setHint\" @complete=\"onFrames\" @error=\"onError\" />\n</template>\n```\n\n### Vanilla / any framework (the core)\n\nUse the core class directly on any square element — this is how you integrate with Svelte, Angular,\nSolid, Expo Web, or no framework at all:\n\n```ts\nimport { LivenessCheck, isLivenessSupported } from '@alternitis-corp/liveness';\n\nif (!isLivenessSupported()) {\n  // show an \"open over HTTPS / allow camera\" message and bail\n}\n\nconst check = new LivenessCheck(document.getElementById('camera')!, {\n  turnThreshold: 0.16,\n  onPrompt: (p) => (hint.textContent = p),\n  onProgress: (p) => (bar.style.width = `${p * 100}%`),\n  onComplete: (frames) => {\n    fetch('/api/liveness', { method: 'POST', body: JSON.stringify({ frames }) });\n  },\n  onError: (e) => console.error(e),\n});\ncheck.start();\n// later / on unmount:\ncheck.stop();\n```\n\n### Plain `<script>` tag (global build, no bundler)\n\n```html\n<div id=\"camera\" style=\"width:260px;height:260px\"></div>\n<script src=\"https://unpkg.com/@alternitis-corp/liveness/dist/liveness.global.js\"></script>\n<script>\n  const check = new Liveness.LivenessCheck(document.getElementById('camera'), {\n    onComplete: (frames) => console.log('captured', frames.length),\n  });\n  check.start();\n</script>\n```\n\n### React Native\n\n- **Expo Web / React‑Native‑Web:** supported — render a host `View`, grab its DOM node, and use the\n  **core** (`new LivenessCheck(node, …)`). (That’s exactly how the Trufy app uses it.)\n- **Native iOS/Android React Native:** not supported — there’s no DOM or `getUserMedia`. Native apps\n  need a native camera + on‑device ML (e.g. `react-native-vision-camera` + a face model). This package\n  targets the **web**.\n\n---\n\n## The guided flow\n\nThe check walks the user through the ordered sequence and reports each step via `onPrompt`:\n\n| Prompt | Show your own copy |\n| --- | --- |\n| `center` | \"Center your face in the circle\" |\n| `turn-left` | \"Slowly turn your head to the left\" |\n| `turn-right` | \"Now slowly turn to the right\" |\n| `look-up` | \"Now tilt your head up\" |\n| `look-down` | \"And tilt your head down\" |\n| `blink` | \"Blink your eyes\" |\n| `open-mouth` | \"Open your mouth\" |\n| `hold` | \"Hold still…\" (finishing up) |\n| `complete` | done — `onComplete(frames)` fires |\n\nThe steps run **in order, one at a time** (set `randomizeChallenges` to shuffle them for anti‑replay),\nand each head step fills only its own ring arc. It completes once every step is cleared, with frames\ncaptured across the angles and a minimum duration elapsed — so it can’t be rushed.\n\n---\n\n## Options\n\n| Option | Default | Description |\n| --- | --- | --- |\n| `size` | `260` | Diameter of the camera circle, px |\n| `turnThreshold` | `0.16` | Yaw magnitude per side (~0–0.5) required for a full turn — higher = turn further |\n| `pitchThreshold` | `0.04` | Pitch deviation from neutral required for a clear look up / down |\n| `invertPitch` | `false` | Flip up/down if a camera reports pitch inverted |\n| `challenges` | `['turn-left','turn-right','look-up','look-down','blink','open-mouth']` | The ordered challenges, one at a time |\n| `randomizeChallenges` | `false` | Shuffle the challenge order each run (anti-replay) |\n| `targetFrames` | `9` | Frames captured across the flow (multi‑angle set) |\n| `minDurationMs` | `4000` | Minimum run time before completing — stops it finishing too fast |\n| `ringColor` | `#5b4dff` | Lit‑tick colour |\n| `ringTrackColor` | `rgba(20,20,40,0.10)` | Unlit‑tick colour |\n| `ringWidth` | `5` | Tick stroke width, px |\n| `mirror` | `true` | Mirror the video (selfie view) |\n| `attribution` | `Powered by Alternitis Corp` | Caption shown at the bottom of the camera; set `\"\"` to hide |\n| `jpegQuality` | `0.9` | Captured‑frame JPEG quality |\n| `model` | CDN | `{ esm, wasm, model }` URL overrides (e.g. to self‑host the MediaPipe assets) |\n\n### Callbacks\n\n- `onPhase(phase)` — `'starting' → 'loading' → 'searching' → 'live' → 'complete'` (or `'error'`)\n- `onPrompt(prompt)` — step guidance (see table above)\n- `onProgress(0..1)` — ring fill\n- `onComplete(frames: string[])` — JPEG data URLs from centre/left/right angles once the check passes\n- `onError(error)` — no secure context, camera blocked, or model failed to load\n\n---\n\n## What you get & verifying it\n\n`onComplete` gives you a **multi‑angle set of full‑resolution JPEG frames** (data URLs). The active\nchallenges (head sweep + randomised blink / mouth / up / down) make a photo, a static dummy head, or a\nnaïve replay fail — but client‑side liveness still runs on an **untrusted device** and is **not** a\nsubstitute for server‑side verification or a dedicated PAD provider against a determined attacker.\nPair the frames with a server‑side check — a vision model and/or a human reviewer (e.g. compare the\nstraight‑on frame to a government‑ID photo), or a purpose‑built liveness vendor — for the actual decision.\n\n## Self‑hosting the model\n\nTo avoid the CDN at runtime, host the MediaPipe assets yourself and pass `model`:\n\n```ts\nnew LivenessCheck(el, {\n  model: {\n    esm: '/vendor/tasks-vision/vision_bundle.mjs',\n    wasm: '/vendor/tasks-vision/wasm',\n    model: '/vendor/face_landmarker.task',\n  },\n});\n```\n\n## Browser support\n\nAny modern browser with `getUserMedia` over HTTPS and WebAssembly: Chrome/Edge, Safari (incl. iOS 16+),\nFirefox. MediaPipe runs via WASM (+ WebGL where available).\n\n---\n\n## Develop / build\n\n```bash\nnpm install\nnpm run build       # tsc → dist (ESM + .d.ts) plus the IIFE global (dist/liveness.global.js)\nnpm run typecheck\n```\n\n## Publishing (maintainers)\n\nScoped public package. Own the `alternitis-corp` npm org, then from this repo:\n\n```bash\nnpm version patch | minor | major\nnpm publish --access public          # or push a v* tag if NPM_TOKEN is set for the CI workflow\n```\n","readmeFilename":"README.md"}