Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 51 additions & 31 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
<!doctype html>
<html lang="en">
<html lang="en" data-bs-theme="dark">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Cool Hand HUD</title>
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta name="description" content="Realtime cyberpunk hand-tracking HUD with neon VFX powered by MediaPipe." />
<meta name="theme-color" content="#0b1220" />
<meta name="color-scheme" content="dark light" />
<title>Cool Hand HUD — Cyberpunk</title>

<!-- Bootstrap 5 -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="icon" type="image/png" href="favicon.png">
<!-- Icons -->
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png">
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png">
Expand All @@ -18,46 +21,57 @@
:root{
--bg:#0b1220;
--panel:#0f1724;
--accent:#6178ff;
--accent-2:#ff6a3d;
--accent:#00f0ff;
--accent-2:#ff3d81;
--glass: rgba(255,255,255,0.04);
--grid: rgba(0,240,255,0.12);
--text:#e6eef8;
}
html,body{height:100%; background: var(--bg); /* Simplified gradient for mobile */ color:#e6eef8; font-family:Inter, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;}
html,body{height:100%; background: var(--bg); /* Simplified gradient for mobile */ color:var(--text); font-family:Inter, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;}
.app-container{max-width:1200px; margin:15px auto;}
.video-card{background: var(--panel); border:1px solid rgba(255,255,255,0.04); border-radius:12px; padding:8px;}
.preview-wrap{position:relative; width:100%; aspect-ratio:4/3; overflow:hidden; border-radius:8px; background:#000; display:flex; align-items:center; justify-content:center;}
.video-card{background: var(--panel); border:1px solid rgba(255,255,255,0.06); border-radius:12px; padding:8px; box-shadow:0 10px 30px rgba(0,0,0,0.35);}
.preview-wrap{position:relative; width:100%; aspect-ratio:4/3; overflow:hidden; border-radius:8px; background:conic-gradient(from 180deg at 50% 50%, rgba(0,240,255,0.08), rgba(255,61,129,0.08), rgba(0,240,255,0.08));}
.preview-wrap::after{content:""; position:absolute; inset:0; pointer-events:none; background: linear-gradient(var(--grid) 1px, transparent 1px), linear-gradient(90deg, var(--grid) 1px, transparent 1px); background-size:24px 24px; mask: radial-gradient(110% 80% at 50% 50%, #000 40%, transparent 100%);}
video#webcam{width:100%; height:100%; object-fit:cover; transform: scaleX(-1); /* mirror */ display:block;}
canvas#overlay{position:absolute; left:0; top:0; transform:scaleX(-1); /* mirror the canvas too */ pointer-events:none; width:100%; height:100%;}
.controls .form-select, .controls .btn {border-radius:8px; padding:8px; touch-action: manipulation;} /* Touch-friendly */
.hud-badge{background:linear-gradient(90deg,var(--accent),var(--accent-2)); color:#071025; font-weight:600; border-radius:999px; padding:6px 10px; box-shadow:0 4px 12px rgba(97,120,255,0.1);}
.controls .form-select, .controls .btn {border-radius:10px; padding:10px; touch-action: manipulation;} /* Touch-friendly */
.hud-badge{background:linear-gradient(90deg,var(--accent),var(--accent-2)); color:#071025; font-weight:700; letter-spacing:0.2px; border-radius:999px; padding:6px 12px; box-shadow:0 4px 18px rgba(0,240,255,0.25);}
.muted{opacity:0.7; font-size:0.9rem;}
.card-panel{background:var(--panel); border:1px solid rgba(255,255,255,0.03);}
.card-panel{background:var(--panel); border:1px solid rgba(255,255,255,0.06);}
footer{opacity:0.6; font-size:0.85rem;}
.small-muted{font-size:0.82rem; color:rgba(230,238,248,0.7);}
.status-dot{display:inline-block; width:10px; height:10px; border-radius:50%;}
.small-muted{font-size:0.82rem; color:rgba(230,238,248,0.8);}
.status-dot{display:inline-block; width:10px; height:10px; border-radius:50%; box-shadow:0 0 8px currentColor;}
.status-on{background: #22c55e;}
.status-off{background: #ef4444;}
.control-row > * {margin-bottom:8px;}
.btn-lg, .btn-sm {min-height: 40px; /* Touch-friendly size */ line-height: 1.5;}
.btn-lg, .btn-sm {min-height: 44px; /* Touch-friendly size */ line-height: 1.5;}
@media (prefers-reduced-motion: reduce){ *{animation:none !important; transition:none !important;} }
/* Responsive tweaks */
@media (max-width: 768px){
.preview-wrap{aspect-ratio:3/4;}
.app-container{margin:10px;}
.video-card, .card-panel{padding:6px;}
.controls .form-select, .controls .btn{font-size:0.9rem;}
.controls .form-select, .controls .btn{font-size:0.95rem;}
.h3{font-size:1.2rem;}
.small-muted{font-size:0.75rem;}
.small-muted{font-size:0.78rem;}
.control-row{display:flex; flex-direction:column; gap:8px;}
.control-row .d-flex{flex-direction:column;}
footer{font-size:0.75rem;}
}
</style>
</head>
<body>
<noscript>
<div class="container app-container">
<div class="alert alert-warning mt-3" role="alert">
JavaScript is required for the hand-tracking HUD. Please enable it to continue.
</div>
</div>
</noscript>
<div class="container app-container">
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<h3 class="mb-0">Cool Hand HUD</h3>
<h3 class="mb-0">Cool Hand HUD <span class="small-muted">Cyberpunk Edition</span></h3>
<div class="small-muted">Realtime hand-tracking HUD with visual effects</div>
</div>
<div class="text-end">
Expand All @@ -81,14 +95,14 @@ <h3 class="mb-0">Cool Hand HUD</h3>
</div>

<div class="preview-wrap" id="previewWrap">
<video id="webcam" playsinline autoplay muted></video>
<canvas id="overlay"></canvas>
<video id="webcam" playsinline autoplay muted aria-label="Webcam preview for hand tracking" title="Webcam preview"></video>
<canvas id="overlay" aria-hidden="true"></canvas>
</div>

<div class="d-flex justify-content-between align-items-center mt-3">
<div>
<button id="startBtn" class="btn btn-lg btn-primary me-2">Enable Camera</button>
<button id="stopBtn" class="btn btn-outline-light btn-lg" disabled>Stop</button>
<button id="startBtn" type="button" class="btn btn-lg btn-primary me-2" aria-pressed="false">Enable Camera</button>
<button id="stopBtn" type="button" class="btn btn-outline-light btn-lg" disabled>Stop</button>
</div>
<div class="text-end">
<button id="snapBtn" class="btn btn-sm btn-light" title="Save screenshot">📸 Screenshot</button>
Expand All @@ -108,7 +122,7 @@ <h6 class="mb-3">Controls</h6>

<div class="control-row">
<label class="form-label mb-1">Performance mode</label>
<select id="modeSelect" class="form-select" title="Performance">
<select id="modeSelect" class="form-select" title="Performance" aria-label="Performance mode">
<option value="mobile">Mobile (lag-free, low res)</option>
<option value="speed" selected>Speed (1 hand, lower res)</option>
<option value="quality">Quality (2 hands, higher res)</option>
Expand All @@ -118,20 +132,20 @@ <h6 class="mb-3">Controls</h6>
<div class="control-row d-flex gap-2">
<div style="flex:1">
<label class="form-label mb-1">Model</label>
<select id="modelSelect" class="form-select">
<select id="modelSelect" class="form-select" aria-label="Model variant">
<option value="full" selected>full (accurate)</option>
<option value="lite">lite (fast)</option>
</select>
</div>
<div style="width:120px">
<label class="form-label mb-1">Max hands</label>
<input id="maxHands" type="number" min="1" max="4" value="1" class="form-control" />
<input id="maxHands" type="number" min="1" max="4" value="1" class="form-control" inputmode="numeric" aria-label="Maximum hands" />
</div>
</div>

<div class="control-row">
<label class="form-label mb-1">Resolution</label>
<select id="resSelect" class="form-select">
<select id="resSelect" class="form-select" aria-label="Resolution">
<option value="320x240">320 x 240</option>
<option value="640x480" selected>640 x 480</option>
<option value="960x720">960 x 720</option>
Expand All @@ -141,18 +155,18 @@ <h6 class="mb-3">Controls</h6>

<div class="control-row">
<label class="form-label mb-1">Frame rate target</label>
<input id="frameRate" type="range" min="10" max="60" value="30" class="form-range" />
<input id="frameRate" type="range" min="10" max="60" value="30" class="form-range" aria-labelledby="frameVal" />
<div class="d-flex justify-content-between small-muted"><span>10</span><span id="frameVal">30</span><span>60</span></div>
</div>

<div class="control-row">
<label class="form-label mb-1">Visuals</label>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="showTrails" checked>
<input class="form-check-input" type="checkbox" id="showTrails" checked aria-checked="true">
<label class="form-check-label small-muted" for="showTrails">Trails</label>
</div>
<div class="form-check form-switch mt-1">
<input class="form-check-input" type="checkbox" id="showParticles" checked>
<input class="form-check-input" type="checkbox" id="showParticles" checked aria-checked="true">
<label class="form-check-label small-muted" for="showParticles">Ambient particles</label>
</div>
</div>
Expand All @@ -165,7 +179,7 @@ <h6 class="mb-3">Controls</h6>

<div class="small-muted">
<div><strong>Logs</strong></div>
<pre id="log" style="height:110px; overflow:auto; background:transparent; border:0; white-space:pre-wrap; color:#cfe9ff;"></pre>
<pre id="log" role="log" aria-live="polite" style="height:110px; overflow:auto; background:transparent; border:0; white-space:pre-wrap; color:#cfe9ff;"></pre>
</div>
</div>
</div>
Expand Down Expand Up @@ -787,8 +801,14 @@ <h6 class="mb-3">Controls</h6>
})();

</script>
<script nomodule>
(function(){
var el = document.getElementById('log');
if(el){ el.textContent = 'Your browser does not support ES modules required for this demo.'; }
})();
</script>

<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" defer></script>
</body>
</html>