diff --git a/src/data/handTrackingConfig.ts b/src/data/handTrackingConfig.ts
index 2b28dda..5133463 100644
--- a/src/data/handTrackingConfig.ts
+++ b/src/data/handTrackingConfig.ts
@@ -14,3 +14,9 @@ export const HAND_TRACKING_BROWSER_DELEGATE: "CPU" | "GPU" = "CPU";
// Absorbs React StrictMode's mount/unmount/mount cycle in dev and rapid
// `nearby` toggles at trigger borders. Invisible to the user (~5 frames).
export const HAND_TRACKING_RUNTIME_START_DELAY_MS = 80;
+
+// How long the hand tracking stays active after the trigger condition
+// (nearby / holding / repair step) turns off. Gives MediaPipe enough time
+// to initialize webcam + model + first frame inference before we cleanup,
+// so the user actually sees their hands when entering a zone briefly.
+export const HAND_TRACKING_LINGER_MS = 2000;
diff --git a/src/providers/gameplay/HandTrackingProvider.tsx b/src/providers/gameplay/HandTrackingProvider.tsx
index b84a375..55b311c 100644
--- a/src/providers/gameplay/HandTrackingProvider.tsx
+++ b/src/providers/gameplay/HandTrackingProvider.tsx
@@ -1,4 +1,6 @@
import type { ReactNode } from "react";
+import { useEffect, useState } from "react";
+import { HAND_TRACKING_LINGER_MS } from "@/data/handTrackingConfig";
import { useSceneMode } from "@/hooks/debug/useSceneMode";
import { useDebugStore } from "@/hooks/debug/useDebugStore";
import { useInteraction } from "@/hooks/interaction/useInteraction";
@@ -38,37 +40,74 @@ export function HandTrackingProvider({
}
});
const { nearby, holding, handHolding } = useInteraction();
- const enabled =
+ const requested =
repairNeedsHands ||
(sceneMode === "physics" && (nearby || holding || handHolding));
- if (!enabled) {
- return (
-
- {children}
-
- );
- }
+ // Keep the runtime active a little after `requested` turns off so
+ // MediaPipe has time to initialize the webcam + model + first frame
+ // before being torn down. Without this, a quick walk-through of a
+ // trigger zone never produces a detected hand and the user sees
+ // nothing.
+ const enabled = useLingeredFlag(requested, HAND_TRACKING_LINGER_MS);
- return {children};
+ // Always render the same JSX root (HandTrackingRuntime). Returning
+ // different element types from this provider would force React to
+ // remount its entire subtree — including the