# 🛡️ Sentinel — Deep Implementation Playbook ## 10 Upgrades: Fully Architected, Battle-Planned **Rule:** Agent does all the thinking. You review the plan, approve or redirect, then agent executes. **Rule:** Visual/UI work requires you to approve a reference style before agent writes any code. --- ## UPGRADE 1: Client-Side Frame Differencing (OpenCV.js) **Goal:** Browser analyzes frames locally, only uploads to server when motion detected. **Files touched:** `sensor_bridge.py` only. **New lines:** ~80 lines of JavaScript. ### Architecture ``` CURRENT FLOW (wasteful): Camera → canvas 320x240 → base64 JPEG → image-data textbox → server process_frame Rate: 2 FPS always = 120 uploads/minute NEW FLOW (smart): Camera → canvas 320x240 → OpenCV.js cv.absdiff(prev, current) → if changed_pixels > threshold → upload base64 to server if not → skip upload, increment local counter Rate: ~0.3-0.8 FPS average = 18-48 uploads/minute ``` ### Implementation Plan **Step 1: Add OpenCV.js CDN load to sensor_bridge.py** Insert at the top of the HTML string (before the IIFE): ```html ``` **Step 2: Add state variables inside the IIFE** ```javascript let prevMat = null; // Previous frame as cv.Mat let cvReady = false; // OpenCV.js loaded flag let skippedFrames = 0; // Count of locally-filtered frames const DIFF_THRESHOLD = 0.12; // 12% pixel change = significant ``` **Step 3: Modify `captureFrame()` function** The current `captureFrame()` always uploads. New version: ``` captureFrame(): 1. Draw video to canvas (same as now) 2. If cvReady: a. Read canvas into cv.Mat (srcMat) b. Convert to grayscale (cv.cvtColor) c. If prevMat exists: - cv.absdiff(prevMat, srcMat, diffMat) - cv.threshold(diffMat, threshMat, 25, 255, cv.THRESH_BINARY) - changedPct = cv.countNonZero(threshMat) / (width * height) - If changedPct < DIFF_THRESHOLD: skippedFrames++ update UI indicator ("Filtered locally: X frames") cleanup Mats, return (DON'T upload) d. prevMat = srcMat.clone() e. cleanup temp Mats 3. Proceed with base64 upload (same as now) ``` **Step 4: Add skipped frame counter to UI** Add a hidden Gradio Number component: ```python # In app.py Monitor tab, alongside other hidden numbers: skipped_frames = gr.Number(value=0, visible=False, elem_id="skipped-frames") ``` JS updates it via `setGradioValue("skipped-frames", skippedFrames)` every 2 seconds. ### Edge Cases - **OpenCV.js fails to load (CDN blocked):** Fall back to current behavior (always upload). Check `window.__cvReady` before using cv. - **First frame:** No prevMat exists → always upload first frame, store as prevMat. - **Mat memory leak:** Always call `.delete()` on temporary Mats. Only keep prevMat alive. - **Camera off:** Reset prevMat to null when camera stops. ### Why This Wins The 240x cost story goes from "trust our architecture" to "watch the counter — 847 frames processed locally, only 4 sent to GPU." That's a demo moment. ### Testing Sequence 1. Deploy with upgrade → activate → wave hand in front of camera → see uploads spike 2. Sit still → see uploads drop to near zero 3. Check that simulation mode still works (it bypasses camera entirely) --- ## UPGRADE 2: Three.js / CSS3D Gyroscope HUD **Goal:** Floating glass HUD overlay on camera feed. Tilts with phone. Shows live telemetry. **Files touched:** `sensor_bridge.py` (~200 lines JS/CSS). **Dependency:** None (pure client-side). ### Architecture Decision **Option A — Pure CSS (Recommended for reliability):** - No external library - CSS `transform: perspective(1000px) rotateX() rotateY()` driven by `deviceorientation` - ~80 lines, impossible to break - Looks great but not "3D" **Option B — Three.js CSS3DRenderer (Recommended for wow):** - Load three.js from CDN (~150KB) - Create CSS3DObject from HUD HTML elements - Rotate entire CSS3D scene based on device orientation - ~200 lines, can break if CDN fails - True 3D parallax effect ### My Recommendation: Option A for reliability, Option B for demo video Start with Option A. It works on every device, including iOS. If you have time, add Option B as an enhancement. ### Implementation Plan (Option A — Pure CSS) **Step 1: Add HUD container HTML to sensor_bridge.py** ```html ``` **Step 2: Add deviceorientation handler** ```javascript // Inside IIFE, after existing sensor handlers: let hudActive = false; const hudEl = document.getElementById("sentinel-hud"); const hudInner = document.getElementById("hud-inner"); function handleHUDOrientation(event) { if (!hudActive || !hudInner) return; // beta = front-back tilt (-180 to 180) // gamma = left-right tilt (-90 to 90) const tiltX = Math.max(-8, Math.min(8, event.gamma * 0.15)); const tiltY = Math.max(-8, Math.min(8, (event.beta - 45) * 0.15)); hudInner.style.transform = `perspective(800px) rotateX(${-tiltY}deg) rotateY(${tiltX}deg)`; } window.addEventListener('deviceorientation', handleHUDOrientation); ``` **Step 3: Update HUD data on each sensor tick** The existing sensor handlers already have access to heading, battery, etc. Add DOM updates: ```javascript // In the existing telemetry update cycle: if (hudActive) { document.getElementById("hud-compass").textContent = `Heading: ${heading}° ${getCompassDir(heading)}`; document.getElementById("hud-battery").textContent = `Battery: ${batteryPct}%`; document.getElementById("hud-threat").textContent = `Threat: ${currentThreatLevel}`; document.getElementById("hud-threat").style.color = currentThreatLevel === "CLEAR" ? "#4ade80" : currentThreatLevel === "WARNING" ? "#fbbf24" : "#ef4444"; } ``` **Step 4: Show HUD on activation** In `bindActivationButton()`, after starting sensors: ```javascript if (hudEl) { hudEl.style.display = "block"; hudActive = true; } ``` ### What You Need to Approve - HUD position: top-center? bottom? full-width strip? - Data fields: compass + threat + FPS + battery? Or different data? - Glass style: subtle (0.06 opacity) or more visible (0.12)? - Color scheme: indigo text (#a5b4fc) or white? ### Testing Sequence 1. Activate on phone → HUD appears floating over camera 2. Tilt phone left → HUD tilts right (parallax) 3. Trigger simulation → HUD threat level changes color 4. Test on iOS → works because it's pure CSS, no WebGL --- ## UPGRADE 3: Binaural 3D Spatial Audio (Web Audio API) **Goal:** Alert voice sounds like it's coming FROM the direction of danger. **Files touched:** `sensor_bridge.py` (~100 lines JS), `app.py` (~10 lines modified). **Dependency:** None (Web Audio API is native to all modern browsers). ### Architecture ``` CURRENT: process_frame → alert_html with