@rick427/react-native-liveness 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,26 +3,25 @@
3
3
  import { useCallback, useEffect, useRef, useState } from 'react';
4
4
  import { Animated, StyleSheet, Text, View } from 'react-native';
5
5
  import { Camera, useCameraDevice, useCameraPermission } from 'react-native-vision-camera';
6
- import { Ellipse, Path, Svg } from 'react-native-svg';
6
+ import { Circle, Path, Svg } from 'react-native-svg';
7
7
  import { useLivenessCamera } from "./useLivenessCamera.js";
8
8
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
- // Oval is sized relative to container WIDTH only, so it stays face-shaped
10
- // on any screen. ry = rx * FACE_RATIO gives a natural portrait face oval.
11
- const OVAL_WIDTH_RATIO = 0.72; // oval width = 72 % of container width
12
- const FACE_RATIO = 1.35; // height-to-width ratio of the oval (~3:4 face)
9
+ // Circle diameter = 82 % of container width large enough to fit any face
10
+ // comfortably without the user needing to fiddle with distance.
11
+ const CIRCLE_DIAMETER_RATIO = 0.82;
13
12
  const STROKE_WIDTH = 3;
14
- // Cubic bezier approximation constant for a smooth ellipse
13
+ // Cubic bezier approximation constant for a smooth circle path
15
14
  const K = 0.5523;
16
15
 
17
16
  /**
18
- * Returns the stroke colour for the oval guide.
17
+ * Returns the stroke colour for the circle guide.
19
18
  *
20
19
  * ● White – no face / scanning (score < 0.4)
21
20
  * ● Yellow – face detected, confidence building (0.4 ≤ score < threshold)
22
21
  * ● Green – liveness confirmed / countdown / capture
23
22
  * ● Red – error
24
23
  */
25
- function getOvalColor(state, score) {
24
+ function getCircleColor(state, score) {
26
25
  switch (state) {
27
26
  case 'error':
28
27
  return '#FF3B30';
@@ -37,14 +36,14 @@ function getOvalColor(state, score) {
37
36
  }
38
37
 
39
38
  /**
40
- * Returns an SVG path string tracing an ellipse (cx, cy, rx, ry) using
41
- * cubic bezier curves. Used inside a compound path with fillRule="evenodd"
42
- * to punch a transparent hole through the dark scrim.
39
+ * Returns an SVG path string tracing a circle at (cx, cy) with radius r
40
+ * using cubic bezier curves. Used inside a compound path with
41
+ * fillRule="evenodd" to punch a transparent hole through the dark scrim.
43
42
  */
44
- function ellipsePath(cx, cy, rx, ry) {
45
- return [`M ${cx + rx} ${cy}`, `C ${cx + rx} ${cy - ry * K} ${cx + rx * K} ${cy - ry} ${cx} ${cy - ry}`, `C ${cx - rx * K} ${cy - ry} ${cx - rx} ${cy - ry * K} ${cx - rx} ${cy}`, `C ${cx - rx} ${cy + ry * K} ${cx - rx * K} ${cy + ry} ${cx} ${cy + ry}`, `C ${cx + rx * K} ${cy + ry} ${cx + rx} ${cy + ry * K} ${cx + rx} ${cy}`, 'Z'].join(' ');
43
+ function circlePath(cx, cy, r) {
44
+ return [`M ${cx + r} ${cy}`, `C ${cx + r} ${cy - r * K} ${cx + r * K} ${cy - r} ${cx} ${cy - r}`, `C ${cx - r * K} ${cy - r} ${cx - r} ${cy - r * K} ${cx - r} ${cy}`, `C ${cx - r} ${cy + r * K} ${cx - r * K} ${cy + r} ${cx} ${cy + r}`, `C ${cx + r * K} ${cy + r} ${cx + r} ${cy + r * K} ${cx + r} ${cy}`, 'Z'].join(' ');
46
45
  }
47
- function OvalOverlay({
46
+ function CircleOverlay({
48
47
  width,
49
48
  height,
50
49
  state,
@@ -52,14 +51,14 @@ function OvalOverlay({
52
51
  }) {
53
52
  if (width === 0 || height === 0) return null;
54
53
  const cx = width / 2;
55
- // Shift centre slightly above midpoint so the face sits naturally in frame
56
- const cy = height * 0.45;
57
- const rx = width * OVAL_WIDTH_RATIO / 2;
58
- const ry = rx * FACE_RATIO;
59
- const color = getOvalColor(state, score);
54
+ // Centre the circle slightly above midpoint so the face sits naturally
55
+ const cy = height * 0.42;
56
+ const r = width * CIRCLE_DIAMETER_RATIO / 2;
57
+ const color = getCircleColor(state, score);
60
58
 
61
- // Compound path: outer rect + oval. evenodd fill rule makes the oval transparent.
62
- const scrimD = `M0 0H${width}V${height}H0Z ${ellipsePath(cx, cy, rx, ry)}`;
59
+ // Compound path: full-screen rect + circle cutout.
60
+ // evenodd fill rule makes the circle area transparent.
61
+ const scrimD = `M0 0H${width}V${height}H0Z ${circlePath(cx, cy, r)}`;
63
62
  return /*#__PURE__*/_jsxs(Svg, {
64
63
  style: StyleSheet.absoluteFill,
65
64
  width: width,
@@ -68,11 +67,10 @@ function OvalOverlay({
68
67
  d: scrimD,
69
68
  fill: "rgba(0,0,0,0.55)",
70
69
  fillRule: "evenodd"
71
- }), /*#__PURE__*/_jsx(Ellipse, {
70
+ }), /*#__PURE__*/_jsx(Circle, {
72
71
  cx: cx,
73
72
  cy: cy,
74
- rx: rx,
75
- ry: ry,
73
+ r: r,
76
74
  fill: "none",
77
75
  stroke: color,
78
76
  strokeWidth: STROKE_WIDTH
@@ -130,7 +128,7 @@ export function LivenessCamera({
130
128
  onError,
131
129
  countdownFrom = 3,
132
130
  livenessThreshold = 0.75,
133
- confirmFrames = 15,
131
+ confirmFrames = 10,
134
132
  soundEnabled = true,
135
133
  style
136
134
  }) {
@@ -207,7 +205,7 @@ export function LivenessCamera({
207
205
  photo: true,
208
206
  pixelFormat: "yuv",
209
207
  fps: 60
210
- }), /*#__PURE__*/_jsx(OvalOverlay, {
208
+ }), /*#__PURE__*/_jsx(CircleOverlay, {
211
209
  width: containerSize.width,
212
210
  height: containerSize.height,
213
211
  state: livenessState,
@@ -1 +1 @@
1
- {"version":3,"names":["useCallback","useEffect","useRef","useState","Animated","StyleSheet","Text","View","Camera","useCameraDevice","useCameraPermission","Ellipse","Path","Svg","useLivenessCamera","jsx","_jsx","jsxs","_jsxs","OVAL_WIDTH_RATIO","FACE_RATIO","STROKE_WIDTH","K","getOvalColor","state","score","ellipsePath","cx","cy","rx","ry","join","OvalOverlay","width","height","color","scrimD","style","absoluteFill","children","d","fill","fillRule","stroke","strokeWidth","CountdownBubble","value","scale","Value","current","opacity","parallel","sequence","spring","toValue","stiffness","damping","useNativeDriver","timing","duration","start","styles","countdownBubble","transform","countdownText","LivenessCamera","onCapture","onLivenessConfirmed","onError","countdownFrom","livenessThreshold","confirmFrames","soundEnabled","hasPermission","requestPermission","device","cameraRef","containerSize","setContainerSize","frameProcessor","livenessState","livenessScore","countdown","feedback","handleLayout","e","nativeEvent","layout","catch","Error","root","centered","permissionText","onLayout","ref","isActive","photo","pixelFormat","fps","feedbackContainer","feedbackText","countdownContainer","captureFlash","pointerEvents","create","flex","backgroundColor","overflow","justifyContent","alignItems","fontSize","textAlign","paddingHorizontal","position","bottom","left","right","fontWeight","textShadowColor","textShadowOffset","textShadowRadius","absoluteFillObject","borderRadius","borderWidth","borderColor","lineHeight"],"sourceRoot":"../../src","sources":["LivenessCamera.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["useCallback","useEffect","useRef","useState","Animated","StyleSheet","Text","View","Camera","useCameraDevice","useCameraPermission","Circle","Path","Svg","useLivenessCamera","jsx","_jsx","jsxs","_jsxs","CIRCLE_DIAMETER_RATIO","STROKE_WIDTH","K","getCircleColor","state","score","circlePath","cx","cy","r","join","CircleOverlay","width","height","color","scrimD","style","absoluteFill","children","d","fill","fillRule","stroke","strokeWidth","CountdownBubble","value","scale","Value","current","opacity","parallel","sequence","spring","toValue","stiffness","damping","useNativeDriver","timing","duration","start","styles","countdownBubble","transform","countdownText","LivenessCamera","onCapture","onLivenessConfirmed","onError","countdownFrom","livenessThreshold","confirmFrames","soundEnabled","hasPermission","requestPermission","device","cameraRef","containerSize","setContainerSize","frameProcessor","livenessState","livenessScore","countdown","feedback","handleLayout","e","nativeEvent","layout","catch","Error","root","centered","permissionText","onLayout","ref","isActive","photo","pixelFormat","fps","feedbackContainer","feedbackText","countdownContainer","captureFlash","pointerEvents","create","flex","backgroundColor","overflow","justifyContent","alignItems","fontSize","textAlign","paddingHorizontal","position","bottom","left","right","fontWeight","textShadowColor","textShadowOffset","textShadowRadius","absoluteFillObject","borderRadius","borderWidth","borderColor","lineHeight"],"sourceRoot":"../../src","sources":["LivenessCamera.tsx"],"mappings":"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","ignoreList":[]}
@@ -6,10 +6,15 @@ const WEIGHTS = {
6
6
  headPose: 0.3,
7
7
  eyesOpen: 0.3
8
8
  };
9
- const FACE_SIZE_MIN = 0.2;
10
- const FACE_SIZE_MAX = 0.65;
11
- const MAX_YAW_DEG = 20;
12
- const MAX_PITCH_DEG = 20;
9
+
10
+ // Loosened from 0.20 / 0.65 — gives more room for the user to breathe
11
+ const FACE_SIZE_MIN = 0.15;
12
+ const FACE_SIZE_MAX = 0.8;
13
+ const FACE_SIZE_SOFT_MARGIN = 0.05; // gradient ramp at each edge
14
+
15
+ // Loosened from 20° — ML Kit head pose is noisy, 25° still rejects clear tilts
16
+ const MAX_YAW_DEG = 25;
17
+ const MAX_PITCH_DEG = 25;
13
18
  export function scoreFrame(face, frameWidth) {
14
19
  if (!face.detected || frameWidth === 0) {
15
20
  return {
@@ -20,10 +25,22 @@ export function scoreFrame(face, frameWidth) {
20
25
  };
21
26
  }
22
27
  const faceWidthRatio = face.bounds.width / frameWidth;
23
- const faceSize = faceWidthRatio >= FACE_SIZE_MIN && faceWidthRatio <= FACE_SIZE_MAX ? 1.0 : 0.0;
24
- const yawOK = Math.abs(face.yawAngle) < MAX_YAW_DEG;
25
- const pitchOK = Math.abs(face.pitchAngle) < MAX_PITCH_DEG;
26
- const headPose = (yawOK ? 0.5 : 0) + (pitchOK ? 0.5 : 0);
28
+
29
+ // Soft-edge face size: smooth ramp into / out of the valid range so a face
30
+ // sitting just outside the threshold doesn't score hard 0.
31
+ let faceSize;
32
+ if (faceWidthRatio >= FACE_SIZE_MIN && faceWidthRatio <= FACE_SIZE_MAX) {
33
+ faceSize = 1.0;
34
+ } else if (faceWidthRatio < FACE_SIZE_MIN) {
35
+ faceSize = Math.max(0, (faceWidthRatio - (FACE_SIZE_MIN - FACE_SIZE_SOFT_MARGIN)) / FACE_SIZE_SOFT_MARGIN);
36
+ } else {
37
+ faceSize = Math.max(0, (FACE_SIZE_MAX + FACE_SIZE_SOFT_MARGIN - faceWidthRatio) / FACE_SIZE_SOFT_MARGIN);
38
+ }
39
+
40
+ // Soft-edge head pose: full score inside threshold, linear decay outside
41
+ const yawScore = Math.abs(face.yawAngle) <= MAX_YAW_DEG ? 1.0 : Math.max(0, 1 - (Math.abs(face.yawAngle) - MAX_YAW_DEG) / MAX_YAW_DEG);
42
+ const pitchScore = Math.abs(face.pitchAngle) <= MAX_PITCH_DEG ? 1.0 : Math.max(0, 1 - (Math.abs(face.pitchAngle) - MAX_PITCH_DEG) / MAX_PITCH_DEG);
43
+ const headPose = (yawScore + pitchScore) / 2;
27
44
 
28
45
  // ML Kit returns -1 when classification is disabled or unavailable
29
46
  const leftEye = face.leftEyeOpenProbability >= 0 ? face.leftEyeOpenProbability : 0.5;
@@ -43,7 +60,7 @@ export function rollingAverage(scores) {
43
60
  }
44
61
  export function getFeedback(face, frameWidth, livenessConfirmed) {
45
62
  if (livenessConfirmed) return 'Liveness confirmed';
46
- if (!face.detected) return 'Position your face in the oval';
63
+ if (!face.detected) return 'Position your face in the circle';
47
64
  const faceWidthRatio = face.bounds.width / frameWidth;
48
65
  if (faceWidthRatio < FACE_SIZE_MIN) return 'Move closer';
49
66
  if (faceWidthRatio > FACE_SIZE_MAX) return 'Move farther away';
@@ -1 +1 @@
1
- {"version":3,"names":["WEIGHTS","faceDetected","faceSize","headPose","eyesOpen","FACE_SIZE_MIN","FACE_SIZE_MAX","MAX_YAW_DEG","MAX_PITCH_DEG","scoreFrame","face","frameWidth","detected","total","faceWidthRatio","bounds","width","yawOK","Math","abs","yawAngle","pitchOK","pitchAngle","leftEye","leftEyeOpenProbability","rightEye","rightEyeOpenProbability","rollingAverage","scores","length","reduce","a","b","getFeedback","livenessConfirmed","yawBad","pitchBad"],"sourceRoot":"../../src","sources":["livenessScoring.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["WEIGHTS","faceDetected","faceSize","headPose","eyesOpen","FACE_SIZE_MIN","FACE_SIZE_MAX","FACE_SIZE_SOFT_MARGIN","MAX_YAW_DEG","MAX_PITCH_DEG","scoreFrame","face","frameWidth","detected","total","faceWidthRatio","bounds","width","Math","max","yawScore","abs","yawAngle","pitchScore","pitchAngle","leftEye","leftEyeOpenProbability","rightEye","rightEyeOpenProbability","rollingAverage","scores","length","reduce","a","b","getFeedback","livenessConfirmed","yawBad","pitchBad"],"sourceRoot":"../../src","sources":["livenessScoring.ts"],"mappings":"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","ignoreList":[]}
@@ -6,6 +6,9 @@ import { Worklets } from 'react-native-worklets-core';
6
6
  import { useLivenessPlugin } from "./LivenessDetector.js";
7
7
  import { getFeedback, rollingAverage, scoreFrame } from "./livenessScoring.js";
8
8
  const WINDOW_SIZE = 20;
9
+ // How many frames to decay consecutiveGood by on a bad frame.
10
+ // Decay (not hard reset) means one noisy frame won't erase all progress.
11
+ const CONSECUTIVE_DECAY = 2;
9
12
  export function useLivenessCamera(options) {
10
13
  const {
11
14
  livenessThreshold,
@@ -25,11 +28,18 @@ export function useLivenessCamera(options) {
25
28
  const frameWidth = useRef(0);
26
29
  const stateRef = useRef('scanning');
27
30
  const isCaptured = useRef(false);
31
+
32
+ // Feedback debouncing: only update the displayed text after the same message
33
+ // has been stable for FEEDBACK_DEBOUNCE_MS. Prevents rapid flickering when
34
+ // ML Kit oscillates between two states on consecutive frames.
35
+ const FEEDBACK_DEBOUNCE_MS = 400;
36
+ const feedbackTimer = useRef(null);
37
+ const pendingFeedback = useRef('Position your face in the circle');
28
38
  const [state, setState] = useState({
29
39
  livenessState: 'scanning',
30
40
  livenessScore: 0,
31
41
  countdown: null,
32
- feedback: 'Position your face in the oval'
42
+ feedback: 'Position your face in the circle'
33
43
  });
34
44
  const setLivenessState = useCallback(next => {
35
45
  stateRef.current = next;
@@ -121,17 +131,32 @@ export function useLivenessCamera(options) {
121
131
  frameScores.current.shift();
122
132
  }
123
133
  const avgScore = rollingAverage(frameScores.current);
134
+
135
+ // Decay on bad frames instead of hard reset — one noisy ML Kit result
136
+ // won't wipe out progress the user has already built up.
124
137
  if (total >= livenessThreshold) {
125
138
  consecutiveGood.current += 1;
126
139
  } else {
127
- consecutiveGood.current = 0;
140
+ consecutiveGood.current = Math.max(0, consecutiveGood.current - CONSECUTIVE_DECAY);
128
141
  }
129
142
  const isLive = consecutiveGood.current >= confirmFrames && avgScore >= livenessThreshold;
130
- const feedback = getFeedback(safeFace, width, isLive);
143
+
144
+ // Debounce the feedback text: only apply a new message after it has been
145
+ // stable for FEEDBACK_DEBOUNCE_MS, preventing rapid label flickering.
146
+ const newFeedback = getFeedback(safeFace, width, isLive);
147
+ if (newFeedback !== pendingFeedback.current) {
148
+ pendingFeedback.current = newFeedback;
149
+ if (feedbackTimer.current) clearTimeout(feedbackTimer.current);
150
+ feedbackTimer.current = setTimeout(() => {
151
+ setState(prev => ({
152
+ ...prev,
153
+ feedback: pendingFeedback.current
154
+ }));
155
+ }, FEEDBACK_DEBOUNCE_MS);
156
+ }
131
157
  setState(prev => ({
132
158
  ...prev,
133
- livenessScore: avgScore,
134
- feedback
159
+ livenessScore: avgScore
135
160
  }));
136
161
  if (isLive && stateRef.current === 'scanning') {
137
162
  setLivenessState('confirmed');
@@ -160,6 +185,7 @@ export function useLivenessCamera(options) {
160
185
  frameScores.current = [];
161
186
  consecutiveGood.current = 0;
162
187
  isCaptured.current = false;
188
+ if (feedbackTimer.current) clearTimeout(feedbackTimer.current);
163
189
  };
164
190
  }, []);
165
191
  return {
@@ -1 +1 @@
1
- {"version":3,"names":["useCallback","useEffect","useMemo","useRef","useState","runAtTargetFps","useFrameProcessor","Worklets","useLivenessPlugin","getFeedback","rollingAverage","scoreFrame","WINDOW_SIZE","useLivenessCamera","options","livenessThreshold","confirmFrames","countdownFrom","soundEnabled","cameraRef","onCapture","onLivenessConfirmed","onError","plugin","frameScores","consecutiveGood","frameWidth","stateRef","isCaptured","state","setState","livenessState","livenessScore","countdown","feedback","setLivenessState","next","current","prev","capture","photo","takePhoto","flash","enableShutterSound","score","timestamp","Date","now","err","Error","String","startCountdown","tick","interval","setInterval","clearInterval","handleFaceData","face","width","safeFace","detected","bounds","x","y","height","yawAngle","pitchAngle","rollAngle","leftEyeOpenProbability","rightEyeOpenProbability","smilingProbability","total","push","length","shift","avgScore","isLive","handleFaceDataJS","createRunOnJS","frameProcessor","frame","detectLiveness"],"sourceRoot":"../../src","sources":["useLivenessCamera.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["useCallback","useEffect","useMemo","useRef","useState","runAtTargetFps","useFrameProcessor","Worklets","useLivenessPlugin","getFeedback","rollingAverage","scoreFrame","WINDOW_SIZE","CONSECUTIVE_DECAY","useLivenessCamera","options","livenessThreshold","confirmFrames","countdownFrom","soundEnabled","cameraRef","onCapture","onLivenessConfirmed","onError","plugin","frameScores","consecutiveGood","frameWidth","stateRef","isCaptured","FEEDBACK_DEBOUNCE_MS","feedbackTimer","pendingFeedback","state","setState","livenessState","livenessScore","countdown","feedback","setLivenessState","next","current","prev","capture","photo","takePhoto","flash","enableShutterSound","score","timestamp","Date","now","err","Error","String","startCountdown","tick","interval","setInterval","clearInterval","handleFaceData","face","width","safeFace","detected","bounds","x","y","height","yawAngle","pitchAngle","rollAngle","leftEyeOpenProbability","rightEyeOpenProbability","smilingProbability","total","push","length","shift","avgScore","Math","max","isLive","newFeedback","clearTimeout","setTimeout","handleFaceDataJS","createRunOnJS","frameProcessor","frame","detectLiveness"],"sourceRoot":"../../src","sources":["useLivenessCamera.ts"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"LivenessCamera.d.ts","sourceRoot":"","sources":["../../../src/LivenessCamera.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAiB,MAAM,SAAS,CAAC;AAsIlE,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,mBAAmB,EACnB,OAAO,EACP,aAAiB,EACjB,iBAAwB,EACxB,aAAkB,EAClB,YAAmB,EACnB,KAAK,GACN,EAAE,mBAAmB,2CAmFrB"}
1
+ {"version":3,"file":"LivenessCamera.d.ts","sourceRoot":"","sources":["../../../src/LivenessCamera.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAiB,MAAM,SAAS,CAAC;AAoIlE,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,mBAAmB,EACnB,OAAO,EACP,aAAiB,EACjB,iBAAwB,EACxB,aAAkB,EAClB,YAAmB,EACnB,KAAK,GACN,EAAE,mBAAmB,2CAmFrB"}
@@ -1 +1 @@
1
- {"version":3,"file":"livenessScoring.d.ts","sourceRoot":"","sources":["../../../src/livenessScoring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAczD,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,GAAG,UAAU,CA6BzE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAGvD;AAED,wBAAgB,WAAW,CACzB,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,MAAM,EAClB,iBAAiB,EAAE,OAAO,GACzB,eAAe,CAmBjB"}
1
+ {"version":3,"file":"livenessScoring.d.ts","sourceRoot":"","sources":["../../../src/livenessScoring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAkBzD,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,GAAG,UAAU,CAsDzE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAGvD;AAED,wBAAgB,WAAW,CACzB,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,MAAM,EAClB,iBAAiB,EAAE,OAAO,GACzB,eAAe,CAmBjB"}
@@ -16,7 +16,7 @@ export type FaceData = {
16
16
  smilingProbability: number;
17
17
  };
18
18
  export type LivenessState = 'idle' | 'scanning' | 'confirmed' | 'countdown' | 'capturing' | 'done' | 'error';
19
- export type FeedbackMessage = 'Position your face in the oval' | 'Move closer' | 'Move farther away' | 'Look straight ahead' | 'Hold still...' | 'Stay still' | 'Open your eyes' | 'Liveness confirmed' | '';
19
+ export type FeedbackMessage = 'Position your face in the circle' | 'Move closer' | 'Move farther away' | 'Look straight ahead' | 'Hold still...' | 'Stay still' | 'Open your eyes' | 'Liveness confirmed' | '';
20
20
  export type CaptureResult = {
21
21
  photo: PhotoFile;
22
22
  livenessScore: number;
@@ -45,7 +45,7 @@ export type LivenessCameraProps = {
45
45
  livenessThreshold?: number;
46
46
  /**
47
47
  * Number of consecutive high-score frames required before liveness is
48
- * confirmed. Defaults to 15 (~500ms at 30fps).
48
+ * confirmed. Defaults to 10 (~500ms at 20fps).
49
49
  */
50
50
  confirmFrames?: number;
51
51
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE5D,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE;QACN,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,uBAAuB,EAAE,MAAM,CAAC;IAChC,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,UAAU,GACV,WAAW,GACX,WAAW,GACX,WAAW,GACX,MAAM,GACN,OAAO,CAAC;AAEZ,MAAM,MAAM,eAAe,GACvB,gCAAgC,GAChC,aAAa,GACb,mBAAmB,GACnB,qBAAqB,GACrB,eAAe,GACf,YAAY,GACZ,gBAAgB,GAChB,oBAAoB,GACpB,EAAE,CAAC;AAEP,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,SAAS,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC;;OAEG;IACH,SAAS,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAE3C;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;IAEjC;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAElB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE5D,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE;QACN,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,uBAAuB,EAAE,MAAM,CAAC;IAChC,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,UAAU,GACV,WAAW,GACX,WAAW,GACX,WAAW,GACX,MAAM,GACN,OAAO,CAAC;AAEZ,MAAM,MAAM,eAAe,GACvB,kCAAkC,GAClC,aAAa,GACb,mBAAmB,GACnB,qBAAqB,GACrB,eAAe,GACf,YAAY,GACZ,gBAAgB,GAChB,oBAAoB,GACpB,EAAE,CAAC;AAEP,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,SAAS,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC;;OAEG;IACH,SAAS,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAE3C;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;IAEjC;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAElB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useLivenessCamera.d.ts","sourceRoot":"","sources":["../../../src/useLivenessCamera.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAS,MAAM,4BAA4B,CAAC;AAKhE,OAAO,KAAK,EACV,aAAa,EAEb,eAAe,EACf,aAAa,EACd,MAAM,SAAS,CAAC;AAIjB,KAAK,OAAO,GAAG;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1C,SAAS,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAC3C,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;IACjC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC,CAAC;AASF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO;;;;;;EAiLjD"}
1
+ {"version":3,"file":"useLivenessCamera.d.ts","sourceRoot":"","sources":["../../../src/useLivenessCamera.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAS,MAAM,4BAA4B,CAAC;AAKhE,OAAO,KAAK,EACV,aAAa,EAEb,eAAe,EACf,aAAa,EACd,MAAM,SAAS,CAAC;AAOjB,KAAK,OAAO,GAAG;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1C,SAAS,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAC3C,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;IACjC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC,CAAC;AASF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO;;;;;;EA4MjD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rick427/react-native-liveness",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Liveness detection library for React Native using Vision Camera v4 and ML Kit",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -5,27 +5,26 @@ import {
5
5
  useCameraDevice,
6
6
  useCameraPermission,
7
7
  } from 'react-native-vision-camera';
8
- import { Ellipse, Path, Svg } from 'react-native-svg';
8
+ import { Circle, Path, Svg } from 'react-native-svg';
9
9
  import { useLivenessCamera } from './useLivenessCamera';
10
10
  import type { LivenessCameraProps, LivenessState } from './types';
11
11
 
12
- // Oval is sized relative to container WIDTH only, so it stays face-shaped
13
- // on any screen. ry = rx * FACE_RATIO gives a natural portrait face oval.
14
- const OVAL_WIDTH_RATIO = 0.72; // oval width = 72 % of container width
15
- const FACE_RATIO = 1.35; // height-to-width ratio of the oval (~3:4 face)
12
+ // Circle diameter = 82 % of container width large enough to fit any face
13
+ // comfortably without the user needing to fiddle with distance.
14
+ const CIRCLE_DIAMETER_RATIO = 0.82;
16
15
  const STROKE_WIDTH = 3;
17
- // Cubic bezier approximation constant for a smooth ellipse
16
+ // Cubic bezier approximation constant for a smooth circle path
18
17
  const K = 0.5523;
19
18
 
20
19
  /**
21
- * Returns the stroke colour for the oval guide.
20
+ * Returns the stroke colour for the circle guide.
22
21
  *
23
22
  * ● White – no face / scanning (score < 0.4)
24
23
  * ● Yellow – face detected, confidence building (0.4 ≤ score < threshold)
25
24
  * ● Green – liveness confirmed / countdown / capture
26
25
  * ● Red – error
27
26
  */
28
- function getOvalColor(state: LivenessState, score: number): string {
27
+ function getCircleColor(state: LivenessState, score: number): string {
29
28
  switch (state) {
30
29
  case 'error':
31
30
  return '#FF3B30';
@@ -40,22 +39,22 @@ function getOvalColor(state: LivenessState, score: number): string {
40
39
  }
41
40
 
42
41
  /**
43
- * Returns an SVG path string tracing an ellipse (cx, cy, rx, ry) using
44
- * cubic bezier curves. Used inside a compound path with fillRule="evenodd"
45
- * to punch a transparent hole through the dark scrim.
42
+ * Returns an SVG path string tracing a circle at (cx, cy) with radius r
43
+ * using cubic bezier curves. Used inside a compound path with
44
+ * fillRule="evenodd" to punch a transparent hole through the dark scrim.
46
45
  */
47
- function ellipsePath(cx: number, cy: number, rx: number, ry: number): string {
46
+ function circlePath(cx: number, cy: number, r: number): string {
48
47
  return [
49
- `M ${cx + rx} ${cy}`,
50
- `C ${cx + rx} ${cy - ry * K} ${cx + rx * K} ${cy - ry} ${cx} ${cy - ry}`,
51
- `C ${cx - rx * K} ${cy - ry} ${cx - rx} ${cy - ry * K} ${cx - rx} ${cy}`,
52
- `C ${cx - rx} ${cy + ry * K} ${cx - rx * K} ${cy + ry} ${cx} ${cy + ry}`,
53
- `C ${cx + rx * K} ${cy + ry} ${cx + rx} ${cy + ry * K} ${cx + rx} ${cy}`,
48
+ `M ${cx + r} ${cy}`,
49
+ `C ${cx + r} ${cy - r * K} ${cx + r * K} ${cy - r} ${cx} ${cy - r}`,
50
+ `C ${cx - r * K} ${cy - r} ${cx - r} ${cy - r * K} ${cx - r} ${cy}`,
51
+ `C ${cx - r} ${cy + r * K} ${cx - r * K} ${cy + r} ${cx} ${cy + r}`,
52
+ `C ${cx + r * K} ${cy + r} ${cx + r} ${cy + r * K} ${cx + r} ${cy}`,
54
53
  'Z',
55
54
  ].join(' ');
56
55
  }
57
56
 
58
- function OvalOverlay({
57
+ function CircleOverlay({
59
58
  width,
60
59
  height,
61
60
  state,
@@ -69,23 +68,22 @@ function OvalOverlay({
69
68
  if (width === 0 || height === 0) return null;
70
69
 
71
70
  const cx = width / 2;
72
- // Shift centre slightly above midpoint so the face sits naturally in frame
73
- const cy = height * 0.45;
74
- const rx = (width * OVAL_WIDTH_RATIO) / 2;
75
- const ry = rx * FACE_RATIO;
76
- const color = getOvalColor(state, score);
71
+ // Centre the circle slightly above midpoint so the face sits naturally
72
+ const cy = height * 0.42;
73
+ const r = (width * CIRCLE_DIAMETER_RATIO) / 2;
74
+ const color = getCircleColor(state, score);
77
75
 
78
- // Compound path: outer rect + oval. evenodd fill rule makes the oval transparent.
79
- const scrimD = `M0 0H${width}V${height}H0Z ${ellipsePath(cx, cy, rx, ry)}`;
76
+ // Compound path: full-screen rect + circle cutout.
77
+ // evenodd fill rule makes the circle area transparent.
78
+ const scrimD = `M0 0H${width}V${height}H0Z ${circlePath(cx, cy, r)}`;
80
79
 
81
80
  return (
82
81
  <Svg style={StyleSheet.absoluteFill} width={width} height={height}>
83
82
  <Path d={scrimD} fill="rgba(0,0,0,0.55)" fillRule="evenodd" />
84
- <Ellipse
83
+ <Circle
85
84
  cx={cx}
86
85
  cy={cy}
87
- rx={rx}
88
- ry={ry}
86
+ r={r}
89
87
  fill="none"
90
88
  stroke={color}
91
89
  strokeWidth={STROKE_WIDTH}
@@ -147,7 +145,7 @@ export function LivenessCamera({
147
145
  onError,
148
146
  countdownFrom = 3,
149
147
  livenessThreshold = 0.75,
150
- confirmFrames = 15,
148
+ confirmFrames = 10,
151
149
  soundEnabled = true,
152
150
  style,
153
151
  }: LivenessCameraProps) {
@@ -212,7 +210,7 @@ export function LivenessCamera({
212
210
  pixelFormat="yuv"
213
211
  fps={60}
214
212
  />
215
- <OvalOverlay
213
+ <CircleOverlay
216
214
  width={containerSize.width}
217
215
  height={containerSize.height}
218
216
  state={livenessState}
@@ -7,10 +7,14 @@ const WEIGHTS = {
7
7
  eyesOpen: 0.3,
8
8
  } as const;
9
9
 
10
- const FACE_SIZE_MIN = 0.2;
11
- const FACE_SIZE_MAX = 0.65;
12
- const MAX_YAW_DEG = 20;
13
- const MAX_PITCH_DEG = 20;
10
+ // Loosened from 0.20 / 0.65 — gives more room for the user to breathe
11
+ const FACE_SIZE_MIN = 0.15;
12
+ const FACE_SIZE_MAX = 0.8;
13
+ const FACE_SIZE_SOFT_MARGIN = 0.05; // gradient ramp at each edge
14
+
15
+ // Loosened from 20° — ML Kit head pose is noisy, 25° still rejects clear tilts
16
+ const MAX_YAW_DEG = 25;
17
+ const MAX_PITCH_DEG = 25;
14
18
 
15
19
  export type FrameScore = {
16
20
  total: number;
@@ -25,14 +29,39 @@ export function scoreFrame(face: FaceData, frameWidth: number): FrameScore {
25
29
  }
26
30
 
27
31
  const faceWidthRatio = face.bounds.width / frameWidth;
28
- const faceSize =
29
- faceWidthRatio >= FACE_SIZE_MIN && faceWidthRatio <= FACE_SIZE_MAX
30
- ? 1.0
31
- : 0.0;
32
32
 
33
- const yawOK = Math.abs(face.yawAngle) < MAX_YAW_DEG;
34
- const pitchOK = Math.abs(face.pitchAngle) < MAX_PITCH_DEG;
35
- const headPose = (yawOK ? 0.5 : 0) + (pitchOK ? 0.5 : 0);
33
+ // Soft-edge face size: smooth ramp into / out of the valid range so a face
34
+ // sitting just outside the threshold doesn't score hard 0.
35
+ let faceSize: number;
36
+ if (faceWidthRatio >= FACE_SIZE_MIN && faceWidthRatio <= FACE_SIZE_MAX) {
37
+ faceSize = 1.0;
38
+ } else if (faceWidthRatio < FACE_SIZE_MIN) {
39
+ faceSize = Math.max(
40
+ 0,
41
+ (faceWidthRatio - (FACE_SIZE_MIN - FACE_SIZE_SOFT_MARGIN)) /
42
+ FACE_SIZE_SOFT_MARGIN
43
+ );
44
+ } else {
45
+ faceSize = Math.max(
46
+ 0,
47
+ (FACE_SIZE_MAX + FACE_SIZE_SOFT_MARGIN - faceWidthRatio) /
48
+ FACE_SIZE_SOFT_MARGIN
49
+ );
50
+ }
51
+
52
+ // Soft-edge head pose: full score inside threshold, linear decay outside
53
+ const yawScore =
54
+ Math.abs(face.yawAngle) <= MAX_YAW_DEG
55
+ ? 1.0
56
+ : Math.max(0, 1 - (Math.abs(face.yawAngle) - MAX_YAW_DEG) / MAX_YAW_DEG);
57
+ const pitchScore =
58
+ Math.abs(face.pitchAngle) <= MAX_PITCH_DEG
59
+ ? 1.0
60
+ : Math.max(
61
+ 0,
62
+ 1 - (Math.abs(face.pitchAngle) - MAX_PITCH_DEG) / MAX_PITCH_DEG
63
+ );
64
+ const headPose = (yawScore + pitchScore) / 2;
36
65
 
37
66
  // ML Kit returns -1 when classification is disabled or unavailable
38
67
  const leftEye =
@@ -61,7 +90,7 @@ export function getFeedback(
61
90
  livenessConfirmed: boolean
62
91
  ): FeedbackMessage {
63
92
  if (livenessConfirmed) return 'Liveness confirmed';
64
- if (!face.detected) return 'Position your face in the oval';
93
+ if (!face.detected) return 'Position your face in the circle';
65
94
 
66
95
  const faceWidthRatio = face.bounds.width / frameWidth;
67
96
  if (faceWidthRatio < FACE_SIZE_MIN) return 'Move closer';
package/src/types.ts CHANGED
@@ -27,7 +27,7 @@ export type LivenessState =
27
27
  | 'error';
28
28
 
29
29
  export type FeedbackMessage =
30
- | 'Position your face in the oval'
30
+ | 'Position your face in the circle'
31
31
  | 'Move closer'
32
32
  | 'Move farther away'
33
33
  | 'Look straight ahead'
@@ -71,7 +71,7 @@ export type LivenessCameraProps = {
71
71
 
72
72
  /**
73
73
  * Number of consecutive high-score frames required before liveness is
74
- * confirmed. Defaults to 15 (~500ms at 30fps).
74
+ * confirmed. Defaults to 10 (~500ms at 20fps).
75
75
  */
76
76
  confirmFrames?: number;
77
77
 
@@ -12,6 +12,9 @@ import type {
12
12
  } from './types';
13
13
 
14
14
  const WINDOW_SIZE = 20;
15
+ // How many frames to decay consecutiveGood by on a bad frame.
16
+ // Decay (not hard reset) means one noisy frame won't erase all progress.
17
+ const CONSECUTIVE_DECAY = 2;
15
18
 
16
19
  type Options = {
17
20
  livenessThreshold: number;
@@ -52,11 +55,20 @@ export function useLivenessCamera(options: Options) {
52
55
  const stateRef = useRef<LivenessState>('scanning');
53
56
  const isCaptured = useRef(false);
54
57
 
58
+ // Feedback debouncing: only update the displayed text after the same message
59
+ // has been stable for FEEDBACK_DEBOUNCE_MS. Prevents rapid flickering when
60
+ // ML Kit oscillates between two states on consecutive frames.
61
+ const FEEDBACK_DEBOUNCE_MS = 400;
62
+ const feedbackTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
63
+ const pendingFeedback = useRef<FeedbackMessage>(
64
+ 'Position your face in the circle'
65
+ );
66
+
55
67
  const [state, setState] = useState<LivenessCameraState>({
56
68
  livenessState: 'scanning',
57
69
  livenessScore: 0,
58
70
  countdown: null,
59
- feedback: 'Position your face in the oval',
71
+ feedback: 'Position your face in the circle',
60
72
  });
61
73
 
62
74
  const setLivenessState = useCallback((next: LivenessState) => {
@@ -143,19 +155,36 @@ export function useLivenessCamera(options: Options) {
143
155
 
144
156
  const avgScore = rollingAverage(frameScores.current);
145
157
 
158
+ // Decay on bad frames instead of hard reset — one noisy ML Kit result
159
+ // won't wipe out progress the user has already built up.
146
160
  if (total >= livenessThreshold) {
147
161
  consecutiveGood.current += 1;
148
162
  } else {
149
- consecutiveGood.current = 0;
163
+ consecutiveGood.current = Math.max(
164
+ 0,
165
+ consecutiveGood.current - CONSECUTIVE_DECAY
166
+ );
150
167
  }
151
168
 
152
169
  const isLive =
153
170
  consecutiveGood.current >= confirmFrames &&
154
171
  avgScore >= livenessThreshold;
155
172
 
156
- const feedback = getFeedback(safeFace, width, isLive);
173
+ // Debounce the feedback text: only apply a new message after it has been
174
+ // stable for FEEDBACK_DEBOUNCE_MS, preventing rapid label flickering.
175
+ const newFeedback = getFeedback(safeFace, width, isLive);
176
+ if (newFeedback !== pendingFeedback.current) {
177
+ pendingFeedback.current = newFeedback;
178
+ if (feedbackTimer.current) clearTimeout(feedbackTimer.current);
179
+ feedbackTimer.current = setTimeout(() => {
180
+ setState((prev) => ({
181
+ ...prev,
182
+ feedback: pendingFeedback.current,
183
+ }));
184
+ }, FEEDBACK_DEBOUNCE_MS);
185
+ }
157
186
 
158
- setState((prev) => ({ ...prev, livenessScore: avgScore, feedback }));
187
+ setState((prev) => ({ ...prev, livenessScore: avgScore }));
159
188
 
160
189
  if (isLive && stateRef.current === 'scanning') {
161
190
  setLivenessState('confirmed');
@@ -198,6 +227,7 @@ export function useLivenessCamera(options: Options) {
198
227
  frameScores.current = [];
199
228
  consecutiveGood.current = 0;
200
229
  isCaptured.current = false;
230
+ if (feedbackTimer.current) clearTimeout(feedbackTimer.current);
201
231
  };
202
232
  }, []);
203
233