{"id":35734510,"url":"https://github.com/sssxyd/face-liveness-detector","last_synced_at":"2026-01-22T11:25:20.948Z","repository":{"id":324147016,"uuid":"1096159280","full_name":"sssxyd/face-liveness-detector","owner":"sssxyd","description":"Pure JS/TS implementation of liveness face detection based on human.js and opencv.js","archived":false,"fork":false,"pushed_at":"2026-01-16T09:58:07.000Z","size":26919,"stargazers_count":31,"open_issues_count":1,"forks_count":5,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-16T23:48:51.137Z","etag":null,"topics":["face-detection","face-recognition","liveness-detection","opencv","pure-javascript","tensorflowjs"],"latest_commit_sha":null,"homepage":"https://face.lowtechsoft.com","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sssxyd.png","metadata":{"files":{"readme":"README.en.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-14T02:45:33.000Z","updated_at":"2026-01-16T09:58:11.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/sssxyd/face-liveness-detector","commit_stats":null,"previous_names":["sssxyd/js-face-detector","sssxyd/face-liveness-detector"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/sssxyd/face-liveness-detector","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sssxyd%2Fface-liveness-detector","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sssxyd%2Fface-liveness-detector/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sssxyd%2Fface-liveness-detector/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sssxyd%2Fface-liveness-detector/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sssxyd","download_url":"https://codeload.github.com/sssxyd/face-liveness-detector/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sssxyd%2Fface-liveness-detector/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28662017,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-22T01:17:37.254Z","status":"online","status_checked_at":"2026-01-22T02:00:07.137Z","response_time":144,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["face-detection","face-recognition","liveness-detection","opencv","pure-javascript","tensorflowjs"],"created_at":"2026-01-06T12:16:37.403Z","updated_at":"2026-01-22T11:25:20.938Z","avatar_url":"https://github.com/sssxyd.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Face Liveness Detection Engine\n\n\u003cdiv align=\"center\"\u003e\n\n\u003e **Languages:** [中文](./README.md) · English\n\n\u003cp\u003e\n  \u003cstrong\u003ePure Frontend Real-time Face Liveness Detection Solution Based on TensorFlow + OpenCV\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp\u003e\n  \u003cimg alt=\"TypeScript\" src=\"https://img.shields.io/badge/TypeScript-5.0+-3178c6?logo=typescript\"\u003e\n  \u003cimg alt=\"NPM Package\" src=\"https://img.shields.io/npm/v/@sssxyd/face-liveness-detector?label=npm\u0026color=cb3837\"\u003e\n  \u003cimg alt=\"License\" src=\"https://img.shields.io/badge/license-MIT-green\"\u003e\n\u003c/p\u003e\n\n\u003c/div\u003e\n\n---\n\n## ✨ Features\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e💯 \u003cstrong\u003ePure Frontend Implementation\u003c/strong\u003e\u003cbr/\u003eZero backend dependency, all processing runs locally in browser\u003c/td\u003e\n    \u003ctd\u003e🔬 \u003cstrong\u003eMixed AI Solution\u003c/strong\u003e\u003cbr/\u003eTensorFlow + OpenCV深度融合\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e🧠 \u003cstrong\u003eDual Liveness Verification\u003c/strong\u003e\u003cbr/\u003eSilent detection + gesture recognition (blink, mouth open, head up, nod)\u003c/td\u003e\n    \u003ctd\u003e⚡ \u003cstrong\u003eEvent-driven Architecture\u003c/strong\u003e\u003cbr/\u003e100% TypeScript, seamlessly integrates with any framework\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e🎯 \u003cstrong\u003eFull-dimensional Analysis\u003c/strong\u003e\u003cbr/\u003eQuality, frontal orientation, motion scores\u003c/td\u003e\n    \u003ctd\u003e🛡️ \u003cstrong\u003eMulti-dimensional Anti-Spoofing\u003c/strong\u003e\u003cbr/\u003ePhoto motion detection, geometric feature analysis\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n---\n\n## 🚀 Online Demo\n\n\u003cdiv align=\"center\"\u003e\n\n**[👉 Live Demo Experience](https://face.lowtechsoft.com/) | Scan QR code with mobile for quick testing**\n\n[![Face Liveness Detection Demo QR Code](https://raw.githubusercontent.com/sssxyd/face-liveness-detector/main/demos/vue-demo/vue-demo.png)](https://face.lowtechsoft.com/)\n\n\u003c/div\u003e\n\n---\n\n## 🧬 Core Algorithm Design\n\n| Detection Module | Technical Solution | Documentation |\n|------------------|--------------------|---------------|\n| **Face Recognition** | Human.js BlazeFace + FaceMesh | 468 facial landmarks + expression recognition |\n| **Face Motion Detection** | Keypoint Motion Analysis | [Face Motion Detection Algorithm](./docs/FaceMovingDetectorAlgorithm.md) - Based on centering and frame-to-frame displacement calculation to detect head movement |\n| **Photo Attack Detection** | Geometric Feature Analysis | [Photo Attack Detection Algorithm](./docs/PhotoAttackDetectorAlgorithm.md) - Perspective consistency, displacement variance, motion consistency analysis |\n| **Gesture Liveness Detection** | Human.js Gesture Module | [Gesture Detection Algorithm](./docs/FaceLivenessDetectionAlgorithm.md) - Random gesture validation including blink, mouth open, nod, head up, etc. |\n\n---\n\n## 📦 Installation Guide\n\n### Quick Install (3 packages)\n\n```\nnpm install @sssxyd/face-liveness-detector @vladmandic/human @techstark/opencv-js\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eOther Package Managers\u003c/strong\u003e\u003c/summary\u003e\n\n```bash\n# Yarn\nyarn add @sssxyd/face-liveness-detector @vladmandic/human @techstark/opencv-js\n\n# pnpm\npnpm add @sssxyd/face-liveness-detector @vladmandic/human @techstark/opencv-js\n```\n\n\u003c/details\u003e\n\n\u003e 📝 **Why Three Packages?**\n\u003e `@vladmandic/human` and `@techstark/opencv-js` are peer dependencies that need to be installed separately to avoid bundling large libraries and reduce final bundle size.\n\n---\n\n## ⚠️ Essential Configuration Steps\n\n### 1️⃣ Fix OpenCV.js ESM Compatibility Issues\n\n`@techstark/opencv-js` contains incompatible UMD format, **patch script must be applied**.\n\n**Reference:**\n- Issue Details: [TechStark/opencv-js#44](https://github.com/TechStark/opencv-js/issues/44)\n- Patch Script: [patch-opencv.js](https://github.com/sssxyd/face-liveness-detector/tree/main/demos/vue-demo/scripts/patch-opencv.js)\n\n**Setup Method (Recommended):** Add to `postinstall` hook in `package.json`\n\n```\n{\n  \"scripts\": {\n    \"postinstall\": \"node patch-opencv.cjs\"\n  }\n}\n```\n\n### 2️⃣ Download Human.js Model Files\n\n`@vladmandic/human` requires model files and TensorFlow WASM backend, otherwise **it won't load**.\n\n**Download Scripts:**\n- Model Copy: [copy-models.js](https://github.com/sssxyd/face-liveness-detector/tree/main/demos/vue-demo/scripts/copy-models.js)\n- WASM Download: [download-wasm.js](https://github.com/sssxyd/face-liveness-detector/tree/main/demos/vue-demo/scripts/download-wasm.js)\n\n**Setup Method (Recommended):** Configure as `postinstall` hook\n\n```\n{\n  \"scripts\": {\n    \"postinstall\": \"node scripts/copy-models.js \u0026\u0026 node scripts/download-wasm.js\"\n  }\n}\n```\n\n---\n\n## 🎯 Quick Start\n\n### Basic Example\n\n```\nimport FaceDetectionEngine, { LivenessAction } from '@sssxyd/face-liveness-detector'\n\n// Initialize engine\nconst engine = new FaceDetectionEngine({\n  // Resource path configuration\n  human_model_path: '/models',\n  tensorflow_wasm_path: '/wasm',\n\n  // Camera resolution settings, default 1280x720, lowering to 640x480 increases speed with slight accuracy loss\n  detect_video_ideal_width: 1280,\n  detect_video_ideal_height: 720,\n\n  // Action liveness detection settings\n  action_liveness_action_count: 1,     // Number of actions required from user, range [0-4], 0 means no action liveness detection, recommend setting to 2 for high reliability\n  action_liveness_verify_timeout: 15000, // Timeout for each action detection, default 15000ms, recommend not less than 1000ms\n\n  // Photo attack detection settings\n  photo_attack_passed_frame_count: 10, // Consecutive X frames passing photo attack detection before final acceptance, default 15, minimum shouldn't be lower than 5, smaller values increase detection speed but decrease accuracy\n})\n\n// Listen to core events\nengine.on('detector-loaded', (data) =\u003e {\n  if (data.success) {\n    console.log('✅ Engine Ready', {\n      opencv: data.opencv_version,\n      human: data.human_version\n    })\n  }\n})\n\nengine.on('detector-info', (data) =\u003e {\n  // Per-frame real-time data\n  console.log({\n    status: data.code,\n    quality: (data.imageQuality * 100).toFixed(1) + '%',\n    frontal: (data.faceFrontal * 100).toFixed(1) + '%',\n    motion: (data.motionScore * 100).toFixed(1) + '%',\n    screen: (data.screenConfidence * 100).toFixed(1) + '%'\n  })\n})\n\nengine.on('detector-action', (data) =\u003e {\n  // Action prompts\n  console.log(`Please perform action: ${data.action} (${data.status})`)\n})\n\nengine.on('detector-finish', (data) =\u003e {\n  // Detection complete\n  if (data.success) {\n    console.log('✅ Liveness verification passed!', {\n      Silent Passed: data.silentPassedCount,\n      Actions Completed: data.actionPassedCount,\n      Best Quality: (data.bestQualityScore * 100).toFixed(1) + '%',\n      Total Time: (data.totalTime / 1000).toFixed(2) + 's'\n    })\n  } else {\n    console.log('❌ Liveness verification failed')\n  }\n})\n\nengine.on('detector-error', (error) =\u003e {\n  console.error(`❌ Error [${error.code}]: ${error.message}`)\n})\n\n// Start detection\nasync function startLivenessDetection() {\n  try {\n    // Initialize library\n    await engine.initialize()\n    \n    // Get video element and start detection\n    const videoEl = document.getElementById('video') as HTMLVideoElement\n    await engine.startDetection(videoEl)\n    \n    // Detection runs automatically until completion or manual stop\n    // engine.stopDetection(true)  // Stop and display best image\n  } catch (error) {\n    console.error('Detection startup failed:', error)\n  }\n}\n\n// Start when ready\nstartLivenessDetection()\n```\n\n---\n\n## ⚙️ Detailed Configuration Reference\n\n### Resource Path Configuration\n\n| Option | Type | Description | Default |\n|--------|------|-------------|---------|\n| `human_model_path` | `string` | Human.js model file directory | `undefined` |\n| `tensorflow_wasm_path` | `string` | TensorFlow WASM file directory | `undefined` |\n| `tensorflow_backend` | `'auto' \\| 'webgl' \\| 'wasm'` | TensorFlow backend engine | `'auto'` |\n\n### Debug Mode Configuration\n\n| Option | Type | Description | Default |\n|--------|------|-------------|---------|\n| `debug_mode` | `boolean` | Enable debug mode | `false` |\n| `debug_log_level` | `'info' \\| 'warn' \\| 'error'` | Minimum debug log level | `'info'` |\n| `debug_log_stages` | `string[]` | Debug log stage filtering (undefined=all) | `undefined` |\n| `debug_log_throttle` | `number` | Debug log throttle interval (ms) | `100` |\n\n### Detection Function Configuration\n\n| Option | Type | Description | Default |\n|--------|------|-------------|---------|\n| `enable_face_moving_detection` | `boolean` | Enable face motion detection | `true` |\n| `enable_photo_attack_detection` | `boolean` | Enable photo attack detection | `true` |\n\n### Video Detection Settings\n\n| Option | Type | Description | Default |\n|--------|------|-------------|---------|\n| `detect_video_ideal_width` | `number` | Video width (pixels) | `1280` |\n| `detect_video_ideal_height` | `number` | Video height (pixels) | `720` |\n| `detect_video_mirror` | `boolean` | Horizontal flip video | `true` |\n| `detect_video_load_timeout` | `number` | Load timeout (ms) | `5000` |\n\n### Face Collection Quality Requirements\n\n| Option | Type | Description | Default |\n|--------|------|-------------|---------|\n| `collect_min_collect_count` | `number` | Minimum collection count | `3` |\n| [collect_min_face_ratio](file:///Users/wangguanda/Downloads/电商-订单/face-liveness-detector/src/config.ts#L115-L115) | `number` | Minimum face ratio (0-1) | `0.5` |\n| `collect_max_face_ratio` | `number` | Maximum face ratio (0-1) | `0.9` |\n| `collect_min_face_frontal` | `number` | Minimum frontal orientation (0-1) | `0.9` |\n| `collect_min_image_quality` | `number` | Minimum image quality (0-1) | `0.5` |\n\n### Face Frontality Parameters\n\n| Option | Type | Description | Default |\n|--------|------|-------------|---------|\n| `yaw_threshold` | `number` | Yaw angle threshold (degrees) | `3` |\n| `pitch_threshold` | `number` | Pitch angle threshold (degrees) | `4` |\n| `roll_threshold` | `number` | Roll angle threshold (degrees) | `2` |\n\n### Image Quality Parameters\n\n| Option | Type | Description | Default |\n|--------|------|-------------|---------|\n| `require_full_face_in_bounds` | `boolean` | Face completely within bounds | `false` |\n| `min_laplacian_variance` | `number` | Minimum Laplacian variance detection value | `40` |\n| `min_gradient_sharpness` | `number` | Minimum gradient sharpness | `0.15` |\n| `min_blur_score` | `number` | Minimum blur score | `0.6` |\n\n### Liveness Detection Settings\n\n| Option | Type | Description | Default |\n|--------|------|-------------|---------|\n| `action_liveness_action_list` | `LivenessAction[]` | Action list | `[BLINK, MOUTH_OPEN, NOD_DOWN, NOD_UP]` |\n| `action_liveness_action_count` | `number` | Number of actions to complete | `1` |\n| `action_liveness_action_randomize` | `boolean` | Randomize action order | `true` |\n| `action_liveness_verify_timeout` | `number` | Single action verification timeout (ms) | `15000` |\n| `action_liveness_min_mouth_open_percent` | `number` | Minimum mouth open percentage (0-1) | `0.2` |\n\n### Photo Attack Detection Settings\n\n| Option | Type | Description | Default |\n|--------|------|-------------|---------|\n| `photo_attack_passed_frame_count` | `number` | Number of consecutive successful frames for photo attack detection | `15` |\n\n\u003e **Note**: Photo attack detection uses built-in geometric feature analysis algorithm (perspective ratio, displacement variance, directional consistency, affine transformation matching), all parameters are internally optimized and require no manual configuration. See [Photo Attack Detection Algorithm Document](./docs/PHOTO_ATTACK_DETECTION_ALGORITHM.md) for details.\n\n---\n\n## 🛠️ API Methods Reference\n\n### Core Methods\n\n#### `initialize(): Promise\u003cvoid\u003e`\nLoad and initialize detection library. **Must be called before using other features.**\n\n```\nawait engine.initialize()\n```\n\n#### `startDetection(videoElement): Promise\u003cvoid\u003e`\nStart face detection on video element.\n\n```\nconst videoEl = document.getElementById('video') as HTMLVideoElement\nawait engine.startDetection(videoEl)\n```\n\n#### `stopDetection(success?: boolean): void`\nStop detection process.\n\n```\nengine.stopDetection(true)  // true: Show best detection image\n```\n\n#### `updateConfig(config): void`\nDynamically update configuration at runtime.\n\n```\nengine.updateConfig({\n  collect_min_face_ratio: 0.6,\n  action_liveness_action_count: 0\n})\n```\n\n#### `getOptions(): FaceDetectionEngineOptions`\nGet current configuration object.\n\n```\nconst config = engine.getOptions()\n```\n\n#### `getEngineState(): EngineState`\nGet current engine state.\n\n```\nconst state = engine.getEngineState()\n```\n\n---\n\n## 📡 Event System\n\nEngine uses **TypeScript Event Emitter Pattern**, all events are type-safe.\n\n### Event List\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003edetector-loaded\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003eEngine initialization completed\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003edetector-info\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003ePer-frame real-time detection data\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003edetector-action\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003eAction liveness prompts and status\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003edetector-finish\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003eDetection complete (success/failure)\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003edetector-error\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003eTriggered when error occurs\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003edetector-debug\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003eDebug information (development)\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n---\n\n### 📋 detector-loaded\n\n**Triggers when engine initialization completes**\n\n```\ninterface DetectorLoadedEventData {\n  success: boolean        // Whether initialization was successful\n  error?: string          // Error message (when failed)\n  opencv_version?: string // OpenCV.js version\n  human_version?: string  // Human.js version\n}\n```\n\n**Example:**\n```\nengine.on('detector-loaded', (data) =\u003e {\n  if (data.success) {\n    console.log('✅ Engine Ready')\n    console.log(`OpenCV ${data.opencv_version} | Human.js ${data.human_version}`)\n  } else {\n    console.error('❌ Initialization failed:', data.error)\n  }\n})\n```\n\n---\n\n### 📊 detector-info\n\n**Returns real-time detection data per frame (high-frequency event)**\n\n```\ninterface DetectorInfoEventData {\n  passed: boolean         // Whether passed silent detection\n  code: DetectionCode     // Detection status code\n  message: string         // Status message\n  faceCount: number       // Number of faces detected\n  faceRatio: number       // Face ratio (0-1)\n  faceFrontal: number     // Face frontal orientation (0-1)\n  imageQuality: number    // Image quality score (0-1)\n  motionScore: number     // Motion score (0-1)\n  keypointVariance: number // Keypoint variance (0-1)\n  motionType: string      // Detected motion type\n  screenConfidence: number // Screen capture confidence (0-1)\n}\n```\n\n**Detection Status Codes:**\n```\nenum DetectionCode {\n  VIDEO_NO_FACE = 'VIDEO_NO_FACE',                // No face detected in video\n  MULTIPLE_FACE = 'MULTIPLE_FACE',                // Multiple faces detected\n  FACE_TOO_SMALL = 'FACE_TOO_SMALL',              // Face too small\n  FACE_TOO_LARGE = 'FACE_TOO_LARGE',              // Face too large\n  FACE_NOT_FRONTAL = 'FACE_NOT_FRONTAL',          // Face not frontal enough\n  FACE_LOW_QUALITY = 'FACE_LOW_QUALITY',          // Image quality too low\n  FACE_IMAGE_CAPTURED = 'FACE_IMAGE_CAPTURED'     // Image captured\n  PHOTO_ATTACK_DETECTED = \"PHOTO_ATTACK_DETECTED\" // Photo attack detected\n}\n```\n\n**Example:**\n```\nengine.on('detector-info', (data) =\u003e {\n  console.log({\n    Detection Status: data.code,\n    Silent Pass: data.passed ? '✅' : '❌',\n    Image Quality: `${(data.imageQuality * 100).toFixed(1)}%`,\n    Face Frontality: `${(data.faceFrontal * 100).toFixed(1)}%`,\n  })\n})\n```\n\n---\n\n### 👤 detector-action\n\n**Action liveness prompts and recognition status**\n\n```\ninterface DetectorActionEventData {\n  action: LivenessAction          // Action to perform\n  status: LivenessActionStatus    // Action status\n}\n\nenum LivenessAction {\n  BLINK = 'blink',           // Blink\n  MOUTH_OPEN = 'mouth_open', // Mouth open\n  NOD_DOWN = 'nod_down',     // Nod down\n  NOD_UP = 'nod_up'          // Nod up\n}\n\nenum LivenessActionStatus {\n  STARTED = 'started',      // Prompt started\n  COMPLETED = 'completed',  // Recognition successful\n  TIMEOUT = 'timeout'       // Recognition timeout\n}\n```\n\n**Example:**\n```\nengine.on('detector-action', (data) =\u003e {\n  const actionLabels = {\n    'blink': 'Blink',\n    'mouth_open': 'Mouth Open',\n    'nod_down': 'Nod Down',\n    'nod_up': 'Nod Up'\n  }\n  \n  switch (data.status) {\n    case 'started':\n      console.log(`👤 Please perform: ${actionLabels[data.action]}`)\n      // Show UI prompt\n      break\n    case 'completed':\n      console.log(`✅ Recognized: ${actionLabels[data.action]}`)\n      // Update progress bar\n      break\n    case 'timeout':\n      console.log(`⏱️ Timeout: ${actionLabels[data.action]}`)\n      // Show retry prompt\n      break\n  }\n})\n```\n\n---\n\n### ✅ detector-finish\n\n**Detection process complete (success or failure)**\n\n```\ninterface DetectorFinishEventData {\n  success: boolean         // Whether verification passed\n  silentPassedCount: number    // Number of silent detections passed\n  actionPassedCount: number    // Number of actions completed\n  totalTime: number        // Total time (milliseconds)\n  bestQualityScore: number // Best image quality (0-1)\n  bestFrameImage: string | null  // Base64 frame image\n  bestFaceImage: string | null   // Base64 face image\n}\n```\n\n**Example:**\n```\nengine.on('detector-finish', (data) =\u003e {\n  if (data.success) {\n    console.log('🎉 Liveness verification successful!', {\n      Silent Passed: `${data.silentPassedCount} times`,\n      Actions Completed: `${data.actionPassedCount} times`,\n      Best Quality: `${(data.bestQualityScore * 100).toFixed(1)}%`,\n      Total Time: `${(data.totalTime / 1000).toFixed(2)}s`\n    })\n    \n    // Upload result to server\n    if (data.bestFrameImage) {\n      uploadToServer({\n        image: data.bestFrameImage,\n        quality: data.bestQualityScore,\n        timestamp: new Date()\n      })\n    }\n  } else {\n    console.log('❌ Verification failed, please retry')\n  }\n})\n```\n\n---\n\n### ⚠️ detector-error\n\n**Error occurred during detection process**\n\n```\ninterface DetectorErrorEventData {\n  code: ErrorCode  // Error code\n  message: string  // Error message\n}\n\nenum ErrorCode {\n  DETECTOR_NOT_INITIALIZED = 'DETECTOR_NOT_INITIALIZED',\n  CAMERA_ACCESS_DENIED = 'CAMERA_ACCESS_DENIED',\n  STREAM_ACQUISITION_FAILED = 'STREAM_ACQUISITION_FAILED',\n  SUSPECTED_FRAUDS_DETECTED = 'SUSPECTED_FRAUDS_DETECTED'\n}\n```\n\n**Example:**\n```\nengine.on('detector-error', (error) =\u003e {\n  const errorMessages: Record\u003cstring, string\u003e = {\n    'DETECTOR_NOT_INITIALIZED': 'Engine not initialized',\n    'CAMERA_ACCESS_DENIED': 'Camera access denied',\n    'STREAM_ACQUISITION_FAILED': 'Failed to acquire camera data stream',\n    'SUSPECTED_FRAUDS_DETECTED': 'Fraudulent activity detected'\n  }\n  \n  console.error(`❌ Error [${error.code}]: ${errorMessages[error.code] || error.message}`)\n  showUserErrorPrompt(errorMessages[error.code])\n})\n```\n\n---\n\n### 🐛 detector-debug\n\n**Debug information for development and troubleshooting**\n\n```\ninterface DetectorDebugEventData {\n  level: 'info' | 'warn' | 'error'  // Log level\n  stage: string                      // Processing stage\n  message: string                    // Debug information\n  details?: Record\u003cstring, any\u003e      // Additional details\n  timestamp: number                  // Unix timestamp\n}\n```\n\n**Example:**\n```\nengine.on('detector-debug', (debug) =\u003e {\n  const time = new Date(debug.timestamp).toLocaleTimeString()\n  const prefix = `[${time}] [${debug.stage}]`\n  \n  if (debug.level === 'error') {\n    console.error(`${prefix} ❌ ${debug.message}`, debug.details)\n  } else {\n    console.log(`${prefix} ℹ️ ${debug.message}`)\n  }\n})\n```\n\n---\n\n## 📖 Type Definitions\n\n### LivenessAction\n```\nenum LivenessAction {\n  BLINK = 'blink',           // Blink\n  MOUTH_OPEN = 'mouth_open', // Mouth open\n  NOD_DOWN = 'nod_down',     // Nod down\n  NOD_UP = 'nod_up'          // Nod up\n}\n```\n\n### LivenessActionStatus\n```\nenum LivenessActionStatus {\n  STARTED = 'started',      // Action prompt started\n  COMPLETED = 'completed',  // Action recognized successfully\n  TIMEOUT = 'timeout'       // Action recognition timeout\n}\n```\n\n### DetectionCode\n```\nenum DetectionCode {\n  VIDEO_NO_FACE = 'VIDEO_NO_FACE',                  // No face detected in video\n  MULTIPLE_FACE = 'MULTIPLE_FACE',                  // Multiple faces detected\n  FACE_TOO_SMALL = 'FACE_TOO_SMALL',                // Face size smaller than minimum threshold\n  FACE_TOO_LARGE = 'FACE_TOO_LARGE',                // Face size larger than maximum threshold\n  FACE_NOT_FRONTAL = 'FACE_NOT_FRONTAL',            // Face angle not frontal enough\n  FACE_LOW_QUALITY = 'FACE_LOW_QUALITY',            // Image quality below minimum\n  FACE_IMAGE_CAPTURED = 'FACE_IMAGE_CAPTURED'       // Face image captured\n  FACE_NOT_MOVING = 'FACE_NOT_MOVING',              // Face not moving \n  PHOTO_ATTACK_DETECTED = 'PHOTO_ATTACK_DETECTED',  // Photo attack detected\n}\n```\n\n\n### ErrorCode\n```\nenum ErrorCode {\n  // Detector initialization failed\n  DETECTOR_NOT_INITIALIZED = 'DETECTOR_NOT_INITIALIZED',\n  // Camera access denied\n  CAMERA_ACCESS_DENIED = 'CAMERA_ACCESS_DENIED',\n  // Video stream acquisition failed\n  STREAM_ACQUISITION_FAILED = 'STREAM_ACQUISITION_FAILED',\n  // Internal error\n  INTERNAL_ERROR = 'INTERNAL_ERROR',\n}\n```\n\n---\n\n## 🎓 Advanced Usage \u0026 Examples\n\n### Complete Vue 3 Demo Project\n\nFor a comprehensive example and advanced usage patterns, please refer to the official demo project:\n\n**[Vue Demo Project](https://github.com/sssxyd/face-liveness-detector/tree/main/demos/vue-demo/)** includes:\n\n- ✅ Complete Vue 3 + TypeScript integration\n- ✅ Real-time detection result visualization\n- ✅ Dynamic configuration panel\n- ✅ Complete handling of all engine events\n- ✅ Real-time debugging panel\n- ✅ Responsive mobile + desktop UI\n- ✅ Error handling and user feedback\n- ✅ Result export and image capture\n\n**Quick Start Demo:**\n\n```\ncd demos/vue-demo\nnpm install\nnpm run dev\n```\n\nThen open the displayed local URL in your browser.\n\n---\n\n## 📥 Local Deployment of Model Files\n\n### Why Local Deployment?\n\n- 🚀 **Performance Boost** - Avoid CDN latency\n- 🔒 **Privacy Protection** - Fully offline operation\n- 🌐 **Network Independence** - Not dependent on external connections\n\n### Available Scripts\n\nTwo download scripts provided in project root:\n\n#### 1️⃣ Copy Human.js Models\n\n```\nnode copy-models.js\n```\n\n**Features:**\n- Copy models from `node_modules/@vladmandic/human/models`\n- Save to `public/models/` directory\n- Includes `.json` and `.bin` model files\n- Automatically displays file size and progress\n\n#### 2️⃣ Download TensorFlow WASM Files\n\n```\nnode download-wasm.js\n```\n\n**Features:**\n- Automatically download TensorFlow.js WASM backend\n- Save to `public/wasm/` directory\n- Download 4 key files:\n  - `tf-backend-wasm.min.js`\n  - `tfjs-backend-wasm.wasm`\n  - `tfjs-backend-wasm-simd.wasm`\n  - `tfjs-backend-wasm-threaded-simd.wasm`\n- **Smart Multi-CDN Sources** automatic fallback:\n  1. unpkg.com (recommended)\n  2. cdn.jsdelivr.net\n  3. esm.sh\n  4. cdn.esm.sh\n\n### Configure Project to Use Local Files\n\nAfter downloading, specify local paths during engine initialization:\n\n```\nconst engine = new FaceDetectionEngine({\n  // Use local files instead of CDN\n  human_model_path: '/models',\n  tensorflow_wasm_path: '/wasm',\n  \n  // Other configurations...\n})\n```\n\n### Automated Setup (Recommended)\n\nConfigure `postinstall` hook in `package.json` for automatic download:\n\n```\n{\n  \"scripts\": {\n    \"postinstall\": \"node scripts/copy-models.js \u0026\u0026 node scripts/download-wasm.js\"\n  }\n}\n```\n\n---\n\n## 🌐 Browser Compatibility\n\n| Browser | Version | Support | Notes |\n|---------|---------|---------|-------|\n| Chrome | 60+ | ✅ | Full Support |\n| Firefox | 55+ | ✅ | Full Support |\n| Safari | 11+ | ✅ | Full Support |\n| Edge | 79+ | ✅ | Full Support |\n\n**System Requirements:**\n\n- 📱 Supports modern browsers with **WebRTC**\n- 🔒 **HTTPS Environment** (localhost OK for development)\n- ⚙️ **WebGL** or **WASM** backend support\n- 📹 **User Authorization** - Requires camera permission\n\n---\n\n## 📄 License\n\n[MIT License](./LICENSE) - Free to use and modify\n\n## 🤝 Contributing\n\nIssues and Pull Requests welcome!\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\n**[⬆ Back to Top](#face-liveness-detection-engine)**\n\nMade with ❤️ by [sssxyd](https://github.com/sssxyd)\n\n\u003c/div\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsssxyd%2Fface-liveness-detector","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsssxyd%2Fface-liveness-detector","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsssxyd%2Fface-liveness-detector/lists"}