perception / frontend /index.html
Zhen Ye
Add military-style radar with helicopter demo mode
d496e6d
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css">
<title>HEL Perception & Engagement Reasoner (Weapon-Grade Demo)</title>
</head>
<body>
<div id="app">
<header>
<div class="brand">
<div class="logo" aria-hidden="true"></div>
<div>
<h1>HEL Perception & Engagement Reasoner</h1>
<div class="sub">Video → detection → expert features → aimpoint + Intensity@Target → HEL feasibility →
closed-loop tracking & dwell control</div>
</div>
</div>
<div class="status-row">
<div class="pill">
<span class="dot" id="sys-dot"></span>
<span id="sys-status">STANDBY · No video loaded</span>
</div>
<div class="pill">
<span class="kbd">Reason</span>
<span>Frame-1 inference</span>
</div>
<div class="pill">
<span class="kbd">Engage</span>
<span>Closed-loop track + dwell</span>
</div>
</div>
</header>
<div class="workspace">
<aside>
<div class="card">
<h2>Video Input</h2>
<div class="hint">Upload one video. Tab 1 uses only the first frame. Tab 2 reuses the same video for tracking
and engagement.</div>
<div class="row mt-md">
<label for="videoFile">Video file</label>
<span class="badge"><span id="videoMeta">No file</span></span>
</div>
<input id="videoFile" type="file" accept="video/*" />
<div class="mt-md">
<label>Mission Objective (optional · enables class filtering)</label>
<textarea id="missionText" rows="3"
placeholder="Optional: e.g., Detect people and vehicles; highlight hazards and key objects."></textarea>
<div class="hint mt-sm">
Mission objective is <b>optional</b>. If provided, it will be used directly as input to the detector.
If left blank, the detector will detect <b>all</b> objects without filtering.
<div class="mini mt-xs" id="hfBackendStatus">HF Backend: STANDBY</div>
</div>
</div>
<div class="btnrow">
<button id="btnLoadSample" class="btn secondary" title="Optional: wire up sample videos later" disabled>Load
Sample</button>
<button id="btnEject" class="btn danger" title="Unload video">Eject</button>
</div>
<div class="grid2">
<div>
<label>Detector</label>
<select id="detectorSelect">
<optgroup label="Object Detection Models">
<option value="hf_yolov8" data-kind="object" selected>Lite</option>
<option value="detr_resnet50" data-kind="object">Big</option>
<option value="grounding_dino" data-kind="object">Large</option>
</optgroup>
<optgroup label="Segmentation Models">
<option value="sam3" data-kind="segmentation">Segmentor</option>
</optgroup>
<optgroup label="Drone Detection Models">
<option value="drone_yolo" data-kind="drone">Drone</option>
</optgroup>
</select>
</div>
<div>
<label>Tracking</label>
<select id="trackerSelect">
<option value="iou">IOU + velocity (built-in)</option>
<option value="external">External hook (user API)</option>
</select>
</div>
<label class="checkbox-row" for="enableDepthToggle">
<input type="checkbox" id="enableDepthToggle">
<span>Enable Legacy Depth Map (Slow)</span>
</label>
<label class="checkbox-row" for="enableGPTToggle" style="margin-top: 4px;">
<input type="checkbox" id="enableGPTToggle">
<span style="color: var(--accent-light);">Enable GPT Reasoning</span>
</label>
<label class="checkbox-row" for="enableStreamToggle" style="margin-top: 4px;">
<input type="checkbox" id="enableStreamToggle" checked>
<span>Enable Stream Processing</span>
</label>
</div>
<div class="hint mt-sm" id="detectorHint">
If the browser model cannot load (offline), plug in your own detector in <span
class="kbd">externalDetect()</span>.
</div>
</div>
<div class="card">
<h2>Mission Intel Summary</h2>
<div class="hint">Unbiased 2–3 sentence scene description computed from a few sampled frames + detected
objects (no location inference).</div>
<div class="intel">
<div class="intel-top">
<span class="badge"><span class="dot warn" id="intelDot"
style="width:7px;height:7px;box-shadow:none"></span><span id="intelStamp">Idle</span></span>
<button id="btnIntelRefresh" class="btn secondary"
style="padding:8px 10px; border-radius:10px; font-weight:700">Refresh</button>
</div>
<div class="thumbrow" aria-label="sampled frames">
<img id="intelThumb0" alt="sample frame 1" />
<img id="intelThumb1" alt="sample frame 2" />
<img id="intelThumb2" alt="sample frame 3" />
</div>
<div id="intelSummaryBox" class="intelbox">Upload a video, then click <b>Reason</b> to generate an unbiased
scene summary.</div>
</div>
</div>
<div class="card">
<h2>HEL & Director Knobs</h2>
<div class="grid2">
<div>
<label>Max output power (kW)</label>
<input id="helPower" type="range" min="20" max="250" step="1" value="60" />
<div class="row"><small class="mini"><span id="helPowerVal">60</span> kW</small><small class="mini">turret
output</small></div>
</div>
<div>
<label>Aperture (m)</label>
<input id="helAperture" type="range" min="0.05" max="0.6" step="0.01" value="0.25" />
<div class="row"><small class="mini"><span id="helApertureVal">0.25</span> m</small><small
class="mini">beam director</small></div>
</div>
</div>
<div class="grid2 mt-sm">
<div>
<label>Beam quality (M²)</label>
<input id="helM2" type="range" min="1.1" max="4.0" step="0.1" value="1.6" />
<div class="row"><small class="mini"><span id="helM2Val">1.6</span></small><small class="mini">lower is
better</small></div>
</div>
<div>
<label>Jitter (μrad RMS)</label>
<input id="helJitter" type="range" min="0.5" max="15" step="0.1" value="3.2" />
<div class="row"><small class="mini"><span id="helJitterVal">3.2</span></small><small
class="mini">director stability</small></div>
</div>
</div>
<div class="grid2 mt-sm">
<div>
<label>Mode</label>
<select id="helMode">
<option value="cw">CW (continuous)</option>
<option value="burst">Burst (duty-limited)</option>
<option value="pulse">Pulsed (peak shaping)</option>
</select>
</div>
<div>
<label>Duty cycle (%)</label>
<input id="helDuty" type="range" min="10" max="100" step="1" value="85" />
<div class="row"><small class="mini"><span id="helDutyVal">85</span>%</small><small class="mini">thermal /
power</small></div>
</div>
</div>
</div>
<div class="card">
<h2>Atmosphere & Maritime</h2>
<div class="grid2">
<div>
<label>Visibility (km)</label>
<input id="atmVis" type="range" min="1" max="30" step="1" value="16" />
<div class="row"><small class="mini"><span id="atmVisVal">16</span> km</small><small
class="mini">aerosol/haze</small></div>
</div>
<div>
<label>Turbulence (Cn²)</label>
<input id="atmCn2" type="range" min="1" max="10" step="1" value="5" />
<div class="row"><small class="mini"><span id="atmCn2Val">5</span>/10</small><small
class="mini">wavefront</small></div>
</div>
</div>
<div class="grid2 mt-sm">
<div>
<label>Sea spray</label>
<input id="seaSpray" type="range" min="0" max="10" step="1" value="2" />
<div class="row"><small class="mini"><span id="seaSprayVal">2</span>/10</small><small class="mini">salt
attenuation</small></div>
</div>
<div>
<label>Adaptive optics</label>
<input id="aoQ" type="range" min="0" max="10" step="1" value="7" />
<div class="row"><small class="mini"><span id="aoQVal">7</span>/10</small><small class="mini">turbulence
mitigation</small></div>
</div>
</div>
<div class="grid2 mt-sm">
<div>
<label>Baseline range (m)</label>
<input id="rangeBase" type="range" min="200" max="6000" step="25" value="1500" />
<div class="row"><small class="mini"><span id="rangeBaseVal">1500</span> m</small><small
class="mini">median target</small></div>
</div>
<div>
<label>Update rate (Hz)</label>
<input id="detHz" type="range" min="1" max="12" step="1" value="6" />
<div class="row"><small class="mini"><span id="detHzVal">6</span> Hz</small><small class="mini">tab 2
detection</small></div>
</div>
</div>
</div>
<div class="card">
<h2>Engagement Policy</h2>
<div class="grid2">
<div>
<label>Targeting</label>
<select id="policyMode">
<option value="auto">Auto: highest lethality margin</option>
<option value="manual">Manual: click target</option>
</select>
</div>
<div>
<label>Assess window (s)</label>
<input id="assessWindow" type="range" min="0.3" max="3.0" step="0.1" value="1.0" />
<div class="row"><small class="mini"><span id="assessWindowVal">1.0</span> s</small><small
class="mini">post-dwell</small></div>
</div>
</div>
<div class="row">
<label>Show agent cursor</label>
<select id="cursorMode">
<option value="on">On</option>
<option value="off">Off</option>
</select>
</div>
<div class="hint">The UI is wired for your APIs. Replace <span class="kbd">externalDetect()</span>, <span
class="kbd">externalFeatures()</span>, and <span class="kbd">externalTrack()</span> when ready.</div>
</div>
<div class="card" style="flex:1; min-height:0">
<h2>System Log</h2>
<div class="log" id="sysLog"></div>
</div>
</aside>
<main>
<div class="tabs">
<button class="tabbtn active" data-tab="frame">Tab 1 · Frame-1 Reason</button>
<button class="tabbtn" data-tab="engage">Tab 2 · Video Engage</button>
<button class="tabbtn" data-tab="trade">Trade Space</button>
</div>
<!-- ===== Tab 1 ===== -->
<section class="tab active" id="tab-frame">
<div class="frame-grid">
<div class="panel panel-monitor">
<h3>
<span>First Frame · Detection + Aimpoints</span>
<span class="rightnote" id="frameNote">Awaiting video</span>
</h3>
<div class="viewbox" id="frameViewBox">
<canvas id="frameCanvas" width="1280" height="720"></canvas>
<canvas id="frameOverlay" class="overlay" width="1280" height="720"></canvas>
<div class="watermark">EO/IR · Track-ID · Aimpoint · Required Dwell</div>
<div class="empty" id="frameEmpty">
<div class="big">Upload a video to begin</div>
<div class="small">This demo performs first-frame perception and engagement reasoning. Then it replays
the same video with closed-loop tracking and dynamic dwell updates.</div>
<div style="display:flex; gap:10px; margin-top:6px; flex-wrap:wrap; justify-content:center;">
<span class="badge"><span class="dot"></span> If you are online, COCO-SSD loads automatically</span>
</div>
</div>
</div>
<div class="btnrow" style="margin-top:10px">
<button id="btnReason" class="btn">Reason</button>
<button id="btnCancelReason" class="btn danger" style="display: none;">Cancel</button>
<button id="btnRecompute" class="btn secondary">Recompute HEL</button>
<button id="btnClear" class="btn secondary">Clear</button>
</div>
<div class="strip mt-md">
<span class="chip" id="chipFrameDepth"
title="Toggle depth view of first frame (if available)">VIEW:DEFAULT</span>
</div>
</div>
<div class="panel panel-objects radar">
<h3>
<span>Radar / Relative Geometry</span>
<span class="rightnote" id="objCount">0</span>
</h3>
<canvas id="frameRadar" width="600" height="260" class="full-size"></canvas>
</div>
<div class="panel panel-features">
<h3>
<span>Selected Target · Features</span>
<span class="rightnote" id="selId"></span>
</h3>
<table class="table" id="featureTable">
<thead>
<tr>
<th style="width:42%">Feature</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td class="k"></td>
<td class="mini">No target selected</td>
</tr>
</tbody>
</table>
<div class="hint mt-sm">You can replace feature generation via <span
class="kbd">externalFeatures()</span>. The UI will render whatever 10–12 key-value pairs you return.
</div>
</div>
<div class="panel panel-summary" style="display:flex; flex-direction:column; min-height: 0;">
<h3>
<span>Object Track Cards</span>
<span class="rightnote" id="trackCount">0</span>
</h3>
<div class="list" id="frameTrackList" style="flex:1; overflow-y:auto; padding:8px;">
<!-- Cards injected here -->
<div style="font-style:italic; color:var(--text-dim); text-align:center; margin-top:20px;">
No objects tracked.
</div>
</div>
</div>
</div>
</section>
<!-- ===== Tab 2 ===== -->
<section class="tab" id="tab-engage">
<div class="engage-grid">
<div class="panel">
<h3>
<span>Video Engage · Tracking + Dynamic Dwell</span>
<div style="display: flex; gap: 8px; align-items: center;">
<button class="collapse-btn" id="btnToggleSidebar">◀ Hide Sidebar</button>
<span class="rightnote" id="engageNote">Awaiting video</span>
</div>
</h3>
<div class="viewbox" style="min-height: 420px;">
<video id="videoEngage" playsinline muted></video>
<canvas id="engageOverlay" class="overlay"></canvas>
<div class="watermark">LOCK · DIST · DWELL · AIMPOINT · FIRE/ASSESS</div>
<div class="empty" id="engageEmpty">
<div class="big">No video loaded</div>
<div class="small">Upload a video. Run <b>Reason</b> first to initialize aimpoints and baseline dwell.
Then click <b>Engage</b>.</div>
</div>
</div>
<div class="btnrow mt-md">
<button id="btnEngage" class="btn">Engage</button>
<button id="btnPause" class="btn secondary">Pause</button>
<button id="btnReset" class="btn secondary">Reset</button>
</div>
<div class="strip mt-md">
<span class="chip" id="chipPolicy">POLICY:AUTO</span>
<span class="chip" id="chipTracks">TRACKS:0</span>
<span class="chip" id="chipBeam">BEAM:OFF</span>
<span class="chip" id="chipHz">DET:6Hz</span>
<span class="chip" id="chipFeed" title="Toggle raw vs HF-processed feed (if available)">FEED:RAW</span>
<span class="chip" id="chipDepth" title="Toggle depth view (if available)">VIEW:DEFAULT</span>
</div>
<div class="mt-md">
<div class="row"><label>Active dwell progress (selected)</label><small class="mini"
id="dwellText"></small>
</div>
<div class="bar">
<div id="dwellBar"></div>
</div>
</div>
<div class="hint mt-md">Manual targeting: choose “Manual” in Engagement Policy, then
click a target in the video. The “beam” will track its aimpoint and accumulate dwell.</div>
</div>
<div class="engage-right">
<div class="panel radar">
<h3>
<span>Radar / Relative Geometry</span>
<span class="rightnote">Dynamic</span>
</h3>
<canvas id="radarCanvas" width="600" height="260" class="full-size"></canvas>
</div>
<div class="panel" style="flex:1; min-height:0">
<h3>
<span>Live Track Cards</span>
<span class="rightnote" id="liveStamp"></span>
</h3>
<div class="list" id="trackList" style="max-height:none"></div>
<!-- Radar Controls -->
<div class="mt-sm" style="padding: 10px; background: rgba(0,0,0,0.2); border-radius: 6px;">
<div class="row">
<label class="mini">History Trails</label>
<input id="radarHistoryLen" type="range" min="0" max="100" value="30" style="flex:1; margin:0 8px">
<small class="mini" id="radarHistoryVal">30</small>
</div>
<div class="row mt-xs">
<label class="mini">Future Pred</label>
<input id="radarFutureLen" type="range" min="0" max="100" value="30" style="flex:1; margin:0 8px">
<small class="mini" id="radarFutureVal">30</small>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ===== Tab 3 ===== -->
<section class="tab" id="tab-trade">
<div class="trade-grid">
<div class="panel plot">
<h3>
<span>Range Sensitivity · Max vs Required Power · Dwell</span>
<span class="rightnote">Interactive</span>
</h3>
<canvas id="tradeCanvas" width="1100" height="420" class="full-size"></canvas>
</div>
<div class="panel">
<h3>
<span>Trade Controls</span>
<span class="rightnote">What-if</span>
</h3>
<div class="hint">This plot is computed from your current HEL and atmosphere knobs. It uses the selected
target’s baseline requirements (from Tab 1) as a reference curve.</div>
<div class="mt-md">
<label>Selected target for curve</label>
<select id="tradeTarget"></select>
</div>
<div class="grid2 mt-sm">
<div>
<label>Range sweep min (m)</label>
<input id="rMin" type="number" value="200" min="50" max="10000" step="50" />
</div>
<div>
<label>Range sweep max (m)</label>
<input id="rMax" type="number" value="6000" min="100" max="20000" step="50" />
</div>
</div>
<div class="row mt-md">
<label>Show P(kill)</label>
<select id="showPk">
<option value="on">On</option>
<option value="off">Off</option>
</select>
</div>
<div class="btnrow">
<button class="btn secondary" id="btnReplot">Replot</button>
<button class="btn secondary" id="btnSnap">Snapshot (log)</button>
</div>
<div class="hint">This tab is designed to look like a weapon trade-space console: propagation, lethality
margin, and dwell inflation with range and atmosphere.</div>
</div>
</div>
</section>
</main>
</div>
<footer>
<div>Demo mode · Unclassified visuals · Integrate your APIs where marked</div>
<div class="mono" id="telemetry">HEL=60kW · VIS=16km · Cn²=5/10 · AO=7/10 · DET=6Hz</div>
</footer>
<!-- Hidden video used only for first-frame capture -->
<video id="videoHidden" playsinline muted style="display:none"></video>
</div>
<script>
window.API_CONFIG = {
BACKEND_BASE: "https://biaslab2025-perception.hf.space"
};
</script>
<script src="./js/init.js"></script>
<script src="./js/core/config.js"></script>
<script src="./js/core/utils.js"></script>
<script src="./js/core/state.js"></script>
<script src="./js/core/physics.js"></script>
<script src="./js/core/video.js"></script>
<script src="./js/core/hel.js"></script>
<script src="./js/ui/logging.js"></script>
<script src="./js/core/tracker.js"></script>
<script src="./js/api/client.js"></script>
<script src="./js/ui/overlays.js"></script>
<script src="./js/ui/radar.js"></script>
<script src="./js/ui/cards.js"></script>
<script src="./js/ui/features.js"></script>
<script src="./js/ui/intel.js"></script>
<script src="./js/ui/cursor.js"></script>
<script src="./js/ui/trade.js"></script>
<script src="./data/helicopter_demo_data.js"></script>
<script src="./js/core/demo.js"></script>
<script src="./js/main.js"></script>
</body>
</html>