1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5<!-- This site is hosted on Netlify. Anyone can build and deploy a site 6 like this one for free: https://netlify.new/?utm_campaign=loops&utm_source=ai-legible&utm_medium=owned&utm_content=comment&utm_id=6e6ad104-124b-49d8-b3c4-4e9574cc849a 7 Netlify hosting facts for this site: static/SSR served via Netlify Edge. --> 8 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 9 <title>VisionAI - Real-Time Webcam Object Detection</title> 10 <!-- Tailwind CSS --> 11
11<script src="https://cdn.tailwindcss.com"></script>
11 12 <!-- TensorFlow.js & COCO-SSD --> 13
13<script src="https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]/dist/tf.min.js"></script>
13 14
14<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/[email protected]/dist/coco-ssd.min.js"></script>
14 15 <!-- FontAwesome Icons --> 16 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> 17 <style> 18 @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;600;800;900&family=Rajdhani:wght@400;600;700&display=swap'); 19 20 body { 21 font-family: 'Rajdhani', sans-serif; 22 background-color: #030712; 23 color: #f3f4f6; 24 overflow-x: hidden; 25 } 26 27 .font-orbitron { 28 font-family: 'Orbitron', sans-serif; 29 } 30 31 /* AI Glowing Aesthetics */ 32 .ai-glow-cyan { 33 box-shadow: 0 0 20px rgba(6, 182, 212, 0.35); 34 } 35 .ai-glow-magenta { 36 box-shadow: 0 0 20px rgba(236, 72, 153, 0.35); 37 } 38 .ai-border-glow { 39 border: 1px solid rgba(6, 182, 212, 0.4); 40 box-shadow: inset 0 0 15px rgba(6, 182, 212, 0.15); 41 } 42 43 /* Scanning Grid Background */ 44 .cyber-grid { 45 background-image: linear-gradient(to right, rgba(6, 182, 212, 0.05) 1px, transparent 1px), 46 linear-gradient(to bottom, rgba(6, 182, 212, 0.05) 1px, transparent 1px); 47 background-size: 30px 30px; 48 } 49 50 /* Animated Scanline Effect */ 51 .scanline { 52 width: 100%; 53 height: 4px; 54 background: linear-gradient(90deg, transparent, rgba(6, 182, 212, 0.8), transparent); 55 position: absolute; 56 top: 0; 57 left: 0; 58 animation: scan 3s linear infinite; 59 z-index: 20; 60 pointer-events: none; 61 } 62 63 @keyframes scan { 64 0% { top: 0%; opacity: 0.8; } 65 50% { opacity: 0.3; } 66 100% { top: 100%; opacity: 0.8; } 67 } 68 69 /* Glassmorphism Panel */ 70 .glass-card { 71 background: rgba(15, 23, 42, 0.75); 72 backdrop-filter: blur(12px); 73 border: 1px solid rgba(255, 255, 255, 0.08); 74 } 75 76 /* HUD Reticle Corners */ 77 .hud-corner { 78 position: absolute; 79 width: 16px; 80 height: 16px; 81 border-color: #06b6d4; 82 pointer-events: none; 83 } 84 .hud-tl { top: 10px; left: 10px; border-top: 3px solid; border-left: 3px solid; } 85 .hud-tr { top: 10px; right: 10px; border-top: 3px solid; border-right: 3px solid; } 86 .hud-bl { bottom: 10px; left: 10px; border-bottom: 3px solid; border-left: 3px solid; } 87 .hud-br { bottom: 10px; right: 10px; border-bottom: 3px solid; border-right: 3px solid; } 88 </style> 89</head> 90<body class="min-h-screen flex flex-col cyber-grid"> 91 92 <!-- Top Highlight Credit Bar --> 93 <header class="bg-gradient-to-r from-cyan-950 via-slate-900 to-fuchsia-950 border-b border-cyan-500/40 text-center py-2.5 px-4 sticky top-0 z-50 shadow-lg shadow-cyan-950/50"> 94 <div class="inline-flex items-center gap-2 px-4 py-1 rounded-full bg-cyan-950/80 border border-cyan-400/50 ai-glow-cyan"> 95 <span class="w-2.5 h-2.5 rounded-full bg-cyan-400 animate-ping"></span> 96 <span class="font-orbitron font-bold text-xs sm:text-sm tracking-wider text-cyan-200"> 97 Made by Pranav Verma, AI-ML Engineer at Ipec Solutions PVT 98 </span> 99 </div> 100 </header> 101 102 <!-- App Navigation Header --> 103 <nav class="px-6 py-4 flex flex-wrap items-center justify-between border-b border-slate-800 bg-slate-950/80 backdrop-blur-md"> 104 <div class="flex items-center space-x-3"> 105 <div class="w-10 h-10 rounded-lg bg-cyan-500/10 border border-cyan-500/40 flex items-center justify-center text-cyan-400 text-xl ai-glow-cyan"> 106 <i class="fa-solid fa-eye"></i> 107 </div> 108 <div> 109 <h1 class="font-orbitron font-black text-2xl tracking-wider bg-clip-text text-transparent bg-gradient-to-r from-cyan-400 via-teal-300 to-fuchsia-500"> 110 VISION<span class="text-white">AI</span> 111 </h1> 112 <p class="text-[10px] text-cyan-400/80 uppercase tracking-widest font-mono">Real-Time Object Detection Neural Engine</p> 113 </div> 114 </div> 115 116 <div class="flex items-center space-x-4 mt-2 sm:mt-0"> 117 <div id="modelStatus" class="flex items-center gap-2 bg-slate-900 border border-slate-700 px-3 py-1.5 rounded-md text-xs font-mono"> 118 <div id="statusDot" class="w-2.5 h-2.5 rounded-full bg-yellow-400 animate-pulse"></div>
119 <span id="statusText" class="text-gray-300">Loading Neural Weights...</span> 120 </div> 121 </div> 122 </nav> 123 124 <!-- Main Workspace Container --> 125 <main class="flex-1 max-w-7xl w-full mx-auto p-4 sm:p-6 grid grid-cols-1 lg:grid-cols-4 gap-6"> 126 127 <!-- Left / Center: Camera Video Canvas Feed --> 128 <div class="lg:col-span-3 flex flex-col space-y-4"> 129 <div class="glass-card rounded-2xl p-4 relative ai-border-glow flex flex-col items-center justify-center min-h-[420px] overflow-hidden"> 130 131 <!-- HUD Corner Accents --> 132 <div class="hud-corner hud-tl"></div> 133 <div class="hud-corner hud-tr"></div> 134 <div class="hud-corner hud-bl"></div> 135 <div class="hud-corner hud-br"></div> 136 137 <!-- Animated Overlay Scanline --> 138 <div id="scanlineEffect" class="scanline hidden"></div> 139 140 <!-- Uninitialized Placeholder --> 141 <div id="placeholder" class="text-center py-20 space-y-4"> 142 <div class="w-20 h-20 mx-auto rounded-full bg-cyan-500/10 border border-cyan-500/30 flex items-center justify-center text-cyan-400 text-3xl animate-pulse"> 143 <i class="fa-solid fa-camera"></i> 144 </div> 145 <div class="space-y-1"> 146 <h3 class="font-orbitron font-bold text-lg text-gray-200">Camera Feed Inactive</h3> 147 <p class="text-xs text-gray-400 max-w-sm mx-auto">Click "Start Neural Vision" below to grant webcam access and trigger real-time TensorFlow.js object inference.</p> 148 </div> 149 </div> 150 151 <!-- Video & Canvas Element Container --> 152 <div id="videoWrapper" class="relative hidden w-full flex items-center justify-center overflow-hidden rounded-xl border border-slate-800 bg-black"> 153 <!-- Raw Webcam Video Stream (Hidden display, rendered through canvas overlay) --> 154 <video id="webcam" autoplay playsinline muted class="hidden"></video> 155 <!-- Display Overlay Canvas --> 156 <canvas id="outputCanvas" class="w-full h-auto object-contain max-h-[70vh] rounded-xl"></canvas> 157 </div> 158 159 </div> 160 161 <!-- Interactive Control Panel --> 162 <div class="glass-card rounded-xl p-4 flex flex-wrap items-center justify-between gap-4 border border-slate-800"> 163 <div class="flex items-center space-x-3"> 164 <button id="toggleCamBtn" disabled onclick="toggleCamera()" 165 class="bg-cyan-500 hover:bg-cyan-400 disabled:bg-slate-800 disabled:text-slate-600 text-slate-950 font-orbitron font-bold text-xs uppercase px-5 py-3 rounded-lg transition duration-200 flex items-center gap-2 ai-glow-cyan"> 166 <i class="fa-solid fa-power-off"></i> 167 <span id="camBtnText">Start Neural Vision</span> 168 </button> 169 170 <button id="facingModeBtn" onclick="switchCameraMode()" 171 class="bg-slate-800 hover:bg-slate-700 text-cyan-300 border border-cyan-500/30 font-semibold text-xs px-4 py-3 rounded-lg transition"> 172 <i class="fa-solid fa-rotate text-xs"></i> Switch Mode 173 </button> 174 </div> 175 176 <!-- Sensitivity Threshold Slider --> 177 <div class="flex items-center space-x-3 bg-slate-900/80 px-4 py-2 rounded-lg border border-slate-800"> 178 <span class="text-xs font-mono text-cyan-400 font-semibold">Min Score:</span> 179 <input type="range" id="thresholdSlider" min="0.2" max="0.9" step="0.05" value="0.5" 180 oninput="updateThreshold(this.value)" 181 class="w-28 accent-cyan-400 cursor-pointer"> 182 <span id="thresholdValue" class="text-xs font-mono text-gray-200 font-bold w-8">50%</span> 183 </div> 184 </div> 185 </div> 186 187 <!-- Right: AI Analytics & Active Detections Panel --> 188 <div class="lg:col-span-1 flex flex-col space-y-4"> 189 190 <!-- Real-Time Metrics Widget --> 191 <div class="glass-card rounded-xl p-4 space-y-3 border border-slate-800"> 192 <h3 class="font-orbitron font-bold text-xs uppercase tracking-wider text-cyan-400 flex items-center gap-2 border-b border-slate-800 pb-2"> 193 <i class="fa-solid fa-chart-simple"></i> System Metrics 194 </h3> 195 <div class="grid grid-cols-2 gap-3"> 196 <div class="bg-slate-900/90 p-3 rounded-lg border border-slate-800"> 197 <p class="text-[10px] text-gray-400 font-mono">INFERENCE FPS</p> 198 <p id="fpsCounter" class="font-orbitron font-extrabold text-xl text-teal-400">0</p> 199 </div> 200 <div class="bg-slate-900/90 p-3 rounded-lg border border-slate-800"> 201 <p class="text-[10px] text-gray-400 font-mono">OBJECTS COUNT</p> 202 <p id="objectCount" class="font-orbitron font-extrabold text-xl text-fuchsia-400">0</p> 203 </div> 204 </div> 205 </div> 206 207 <!-- Live Detections Feed Stream --> 208 <div class="glass-card rounded-xl p-4 flex-1 flex flex-col border border-slate-800 min-h-[300px]"> 209 <div class="flex items-center justify-between border-b border-slate-800 pb-2 mb-3"> 210 <h3 class="font-orbitron font-bold text-xs uppercase tracking-wider text-cyan-400 flex items-center gap-2"> 211 <i class="fa-solid fa-microchip"></i> Detected Classes 212 </h3>
213 <span class="text-[10px] bg-cyan-950 text-cyan-400 border border-cyan-800 px-2 py-0.5 rounded font-mono">COCO-SSD</span> 214 </div> 215 216 <div id="detectionList" class="flex-1 overflow-y-auto space-y-2 pr-1 text-xs"> 217 <p class="text-gray-500 italic text-center py-10 font-mono">No active detections detected.</p> 218 </div> 219 </div> 220 221 </div> 222 223 </main> 224 225 <!-- Footer --> 226 <footer class="bg-slate-950 border-t border-slate-900 text-center py-4 text-xs text-slate-500 space-y-1"> 227 <p class="font-orbitron font-semibold text-slate-400">Made by Pranav Verma, AI-ML Engineer at Ipec Solutions PVT</p> 228 <p class="text-[11px] font-mono">Client-Side Object Detection ⢠TensorFlow.js ⢠MobileNet SSD</p> 229 </footer> 230 231 <!-- TensorFlow.js & UI Logic --> 232
232<script> 233 let detectorModel = null; 234 let video = null; 235 let canvas = null; 236 let ctx = null; 237 let isStreaming = false; 238 let animationFrameId = null; 239 let minScoreThreshold = 0.5; 240 let lastFrameTime = performance.now(); 241 let frameCount = 0; 242 let currentFacingMode = 'user'; // 'user' or 'environment' 243 244 window.addEventListener('DOMContentLoaded', async () => { 245 video = document.getElementById('webcam'); 246 canvas = document.getElementById('outputCanvas'); 247 ctx = canvas.getContext('2d'); 248 249 await initializeModel(); 250 }); 251 252 // Load COCO-SSD Object Detection Model via TensorFlow.js 253 async function initializeModel() { 254 const statusDot = document.getElementById('statusDot'); 255 const statusText = document.getElementById('statusText'); 256 const toggleCamBtn = document.getElementById('toggleCamBtn'); 257 258 try { 259 // Ensure backend is initialized 260 await tf.ready(); 261 statusText.innerText = "Loading Neural Architecture..."; 262 263 // Load model weights 264 detectorModel = await cocoSsd.load({ base: 'lite_mobilenet_v2' }); 265 266 statusDot.classList.remove('bg-yellow-400'); 267 statusDot.classList.add('bg-cyan-400'); 268 statusText.innerText = "COCO-SSD Ready"; 269 statusText.classList.add('text-cyan-300', 'font-bold'); 270 271 toggleCamBtn.disabled = false; 272 } catch (err) { 273 console.error("Model Load Error:", err); 274 statusDot.classList.remove('bg-yellow-400'); 275 statusDot.classList.add('bg-red-500'); 276 statusText.innerText = "Failed to load model"; 277 } 278 } 279 280 // Toggle Webcam On/Off 281 async function toggleCamera() { 282 if (isStreaming) { 283 stopWebcam(); 284 } else { 285 await startWebcam(); 286 } 287 } 288 289 async function startWebcam() { 290 if (!detectorModel) return; 291 292 try { 293 const constraints = { 294 video: { 295 facingMode: currentFacingMode, 296 width: { ideal: 1280 }, 297 height: { ideal: 720 } 298 }, 299 audio: false 300 }; 301 302 const stream = await navigator.mediaDevices.getUserMedia(constraints); 303 video.srcObject = stream; 304 305 await new Promise((resolve) => { 306 video.onloadedmetadata = () => { 307 video.play(); 308 resolve(); 309 }; 310 }); 311 312 // Match canvas aspect ratio with camera feed 313 canvas.width = video.videoWidth; 314 canvas.height = video.videoHeight; 315 316 document.getElementById('placeholder').classList.add('hidden'); 317 document.getElementById('videoWrapper').classList.remove('hidden');
318 document.getElementById('scanlineEffect').classList.remove('hidden'); 319 320 document.getElementById('camBtnText').innerText = "Stop Vision Engine"; 321 document.getElementById('toggleCamBtn').classList.remove('bg-cyan-500'); 322 document.getElementById('toggleCamBtn').classList.add('bg-fuchsia-600', 'hover:bg-fuchsia-500'); 323 324 isStreaming = true; 325 detectObjectsLoop(); 326 } catch (err) { 327 console.error("Webcam Access Error:", err); 328 alert("Camera permission denied or camera device not accessible."); 329 } 330 } 331 332 function stopWebcam() { 333 isStreaming = false; 334 if (animationFrameId) { 335 cancelAnimationFrame(animationFrameId); 336 } 337 338 if (video.srcObject) { 339 video.srcObject.getTracks().forEach(track => track.stop()); 340 video.srcObject = null; 341 } 342 343 ctx.clearRect(0, 0, canvas.width, canvas.height); 344 345 document.getElementById('videoWrapper').classList.add('hidden'); 346 document.getElementById('placeholder').classList.remove('hidden');
347 document.getElementById('scanlineEffect').classList.add('hidden'); 348 349 document.getElementById('camBtnText').innerText = "Start Neural Vision"; 350 document.getElementById('toggleCamBtn').classList.remove('bg-fuchsia-600', 'hover:bg-fuchsia-500'); 351 document.getElementById('toggleCamBtn').classList.add('bg-cyan-500'); 352 353 document.getElementById('fpsCounter').innerText = "0"; 354 document.getElementById('objectCount').innerText = "0"; 355 document.getElementById('detectionList').innerHTML = '<p class="text-gray-500 italic text-center py-10 font-mono">Camera stream stopped.</p>'; 356 } 357 358 async function switchCameraMode() { 359 currentFacingMode = (currentFacingMode === 'user') ? 'environment' : 'user'; 360 if (isStreaming) { 361 stopWebcam(); 362 await startWebcam(); 363 } 364 } 365 366 function updateThreshold(val) { 367 minScoreThreshold = parseFloat(val); 368 document.getElementById('thresholdValue').innerText = `${Math.round(val * 100)}%`; 369 } 370 371 // Real-Time Detection Frame Loop 372 async function detectObjectsLoop() { 373 if (!isStreaming) return; 374 375 // Calculate FPS 376 const now = performance.now(); 377 frameCount++; 378 if (now - lastFrameTime >= 1000) { 379 document.getElementById('fpsCounter').innerText = frameCount; 380 frameCount = 0; 381 lastFrameTime = now; 382 } 383 384 if (video.readyState === 4) { 385 // Execute prediction 386 const predictions = await detectorModel.detect(video); 387 388 // Filter predictions based on minimum threshold 389 const filteredPredictions = predictions.filter(p => p.score >= minScoreThreshold); 390 391 // Render bounding boxes & update HUD 392 drawDetections(filteredPredictions); 393 updateDetectionSidebar(filteredPredictions); 394 } 395 396 animationFrameId = requestAnimationFrame(detectObjectsLoop); 397 } 398 399 // Draw Bounding Boxes & HUD Overlays 400 function drawDetections(predictions) { 401 ctx.clearRect(0, 0, canvas.width, canvas.height); 402 403 // Draw current video frame onto canvas 404 ctx.drawImage(video, 0, 0, canvas.width, canvas.height); 405 406 predictions.forEach((prediction, index) => { 407 const [x, y, width, height] = prediction.bbox; 408 const scorePercent = Math.round(prediction.score * 100); 409 410 // Color Palette by object class hash 411 const colorHue = (prediction.class.length * 55) % 360; 412 const mainColor = `hsla(${colorHue}, 90%, 60%, 0.9)`; 413 const fillColor = `hsla(${colorHue}, 90%, 60%, 0.15)`; 414 415 // Draw Translucent Box 416 ctx.fillStyle = fillColor; 417 ctx.fillRect(x, y, width, height); 418 419 // Draw Border 420 ctx.strokeStyle = mainColor; 421 ctx.lineWidth = 3; 422 ctx.strokeRect(x, y, width, height); 423 424 // Draw Corner Accents 425 const lineLength = Math.min(15, width / 4, height / 4); 426 ctx.strokeStyle = "#FFFFFF"; 427 ctx.lineWidth = 4; 428 429 // Top Left Corner 430 ctx.beginPath(); 431 ctx.moveTo(x, y + lineLength); ctx.lineTo(x, y); ctx.lineTo(x + lineLength, y); 432 ctx.stroke(); 433 434 // Top Right Corner 435 ctx.beginPath(); 436 ctx.moveTo(x + width - lineLength, y); ctx.lineTo(x + width, y); ctx.lineTo(x + width, y + lineLength); 437 ctx.stroke(); 438 439 // Draw Tag Background 440 const labelText = `${prediction.class.toUpperCase()} ${scorePercent}%`; 441 ctx.font = 'bold 12px Orbitron, sans-serif'; 442 const textWidth = ctx.measureText(labelText).width; 443 const textHeight = 18; 444 445 ctx.fillStyle = mainColor; 446 ctx.fillRect(x, y > textHeight ? y - textHeight - 4 : y, textWidth + 12, textHeight + 4); 447 448 // Draw Tag Text 449 ctx.fillStyle = "#000000"; 450 ctx.fillText(labelText, x + 6, y > textHeight ? y - 8 : y + 12); 451 }); 452 453 document.getElementById('objectCount').innerText = predictions.length; 454 } 455 456 // Render Live Detection List on Sidebar 457 function updateDetectionSidebar(predictions) { 458 const listContainer = document.getElementById('detectionList'); 459 460 if (predictions.length === 0) { 461 listContainer.innerHTML = '<p class="text-gray-500 italic text-center py-10 font-mono">Scanning target area...</p>'; 462 return; 463 } 464 465 listContainer.innerHTML = ''; 466 predictions.forEach(p => { 467 const scorePercent = Math.round(p.score * 100); 468 const itemEl = document.createElement('div'); 469 itemEl.className = "flex items-center justify-between p-2.5 rounded-lg bg-slate-900/90 border border-cyan-500/30 hover:border-cyan-400 transition"; 470 471 itemEl.innerHTML = ` 472 <div class="flex items-center space-x-2">
473 <span class="w-2 h-2 rounded-full bg-cyan-400 animate-pulse"></span> 474 <span class="font-orbitron font-bold text-gray-200 uppercase">${p.class}</span> 475 </div> 476 <span class="font-mono font-semibold text-cyan-300 bg-cyan-950 px-2 py-0.5 rounded border border-cyan-800">${scorePercent}%</span> 477 `; 478 listContainer.appendChild(itemEl); 479 }); 480 } 481 </script>
481 482</body> 483</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.