Imported from zubershk/TrackVision (
AGENTS.md). Install upstream withnpx skills add zubershk/TrackVision. Copyright stays with the author.
TrackVision AGENTS.md
Commands
npm install— install dependencies (npm only;package-lock.jsonis the single lockfile)npm run dev— Vite dev server on port 3000, host 0.0.0.0npm run dev:https— same but with self-signed TLS (@vitejs/plugin-basic-ssl, envTV_SSL=1) — required for camera access from phones/other devices on the LAN (browsers blockgetUserMediaon plain HTTP non-localhost origins)npm run lint—tsc --noEmitonly; the sole verification step (no ESLint despite README claims)npm run test— Vitest (49 unit tests across kalman/matching/tracker/workerUtils/store);npm run test:watchfor watch mode; config invitest.config.ts(node env,src/**/*.test.ts)npm run build— runsscripts/verify-models.jsfirst and fails ifpublic/models/yolov8n.onnxis missing, <100 KB (placeholder), or lacks the ONNX magic byte0x08npm run check-models— run that model gate standalonenode scripts/download-models.js— full-size models intopublic/models/(onlyyolov8n.onnxships real; other three are tiny placeholders)npm run clean— cross-platform (scripts/clean.js, works on Windows)
Environment
- Fully client-side; no env vars required (README's
VITE_HF_TOKEN/VITE_MODEL_CDNare not read by any code). - Camera permission required for tracking; needs HTTPS or localhost.
- Dev server sets COOP
same-origin+ COEPrequire-corpheaders (vite.config) — required for SharedArrayBuffer/WebGPU. Prod equivalents live innetlify.toml/vercel.json; servingdist/any other way needs them set manually. - vite.config
optimizeDeps: includesonnxruntime-web.
Architecture Overview
- Entry:
App.tsxrendersLandingorCommandCenterbased onuseVisionStore.mode→useVisionEngine.ts(per-frame rAF loop) → detection + tracker workers. - Two vision modes (
visionModein store):fast= YOLOv8n ONNX viaworkers/yoloDetectionWorker.ts(COCO-80 fromCOCO_CLASSESinworkerUtils.ts);open= YOLO-World + CLIP — fully functional since the CLIP wiring: the export takesimages+text_features [1,C,512];workers/clipTokenizer.tsis a real CLIP BPE tokenizer (assetspublic/models/clip-tokenizer/vocab.json+merges.txt, git-tracked; ids verified identical vs Xenova reference — NFC + lowercase +</w>word-final suffix), prompts are"a photo of a {concept}", localclip_text_encoder.onnxruns int64 input_ids on wasm, embeddings are L2-normalized and fed as the second input. Outputs are post-sigmoidscores [1,N,C]+ XYXYboxes [1,N,4]in letterbox space, decoded bypostprocessWorld(separate from fast-pathpostprocess). Placeholder gate remains; DETECT before concepts finish encoding returns a graceful error. - Four active workers, each spawned by a hook in
src/hooks/with{ type, payload, msgId }postMessage protocol: yoloDetection, yoloWorld, reid (OSNet), tracker (ByteTrack). - ONNX session fallback cascade is WebNN → WebGPU → WebGL → WASM (always last), not just WebGPU→WASM — see
createAcceleratedSessioninworkers/workerUtils.ts. - Class-aware NMS runs on the main thread:
applyNMSWithClassimported fromworkers/workerUtils.ts(type-only ort import; safe on main thread). - Shared types live in
src/types.ts(BBox,Detection,CapturedFrame);lib/kalman|matching|tracker,workers/workerUtils,workers/trackerWorkerandstore.tsre-export them. - State: two Zustand stores —
src/store.ts(useVisionStore: tracking state, frames, telemetry) andsrc/store/modelInitStore.ts(useModelInitStore: boot/init overlay subsystem progress).
Important Gotchas
- ReID IS wired now: after NMS the engine slices a copy of the 640×640 pixels before detection transfers the frame buffer, maps video-space boxes into letterbox coords (
scale/offsetX/offsetY), and callsextractBatch; embeddings attach only when non-zero and lengths match. Gated onreidReady && !reidIsFallback— placeholder/stub OSNet never feeds appearance matching. Tracker fuses via EMA α=0.3 + cosine cost weight 0.3. - Tracker thresholds: engine initializes with
trackThresh = Math.min(confidenceThreshold, 0.4)(hard cap at 0.4), single sourceTRACKER_CONFIGinuseVisionEngine.ts(matchThresh = 0.7,maxTimeLost = 30). Confirmation requireshits >= 3OR two detections scoring ≥ 0.7 — seeTrack.updateinlib/tracker.ts. - Worker protocol: init timeout 60 s; detect timeouts (8–10 s) resolve gracefully with empty results instead of throwing. Frame
ArrayBuffers are transferred per DETECT call (detached afterward). - Camera: frame processing waits for
video.readyState >= 2and non-zero dimensions. Camera failures surface as a UI banner inCameraHUD(HTTPS hint on insecure origins) instead of console-only errors — phones on plain HTTP get a hard browser block, which is what made Vision/Analytics/History look "dead" on mobile. - Label stabilization: track class = majority vote over last 10 labels + EMA-smoothed bbox/score.
- Replay mode:
isReplaypauses the live loop;currentTimescrubs storedframes[]. - SW registers only in prod builds (
import.meta.env.PRODguard inmain.tsx) so Vite HMR isn't intercepted. - Models on disk: only
yolov8n.onnxis git-tracked; the three optional models are.gitignored (they total ~680 MB once downloaded). Downloader validates size + ONNX magic byte and skips valid cached files unless--force.
Key Conventions
BBox=[number, number, number, number](x, y, w, h) in pixel coordinates.- Frames captured letterboxed at 640×640 via
useOffscreenCanvas.captureFrame(returns buffer + scale/offset info used to map coords back to video space). - History window: 5 minutes (
maxHistoryMs); old frames pruned on eachaddFrameData. cn()utility (src/lib/utils.ts) wrapsclsx+tailwind-merge.