Skip to content

Instantly share code, notes, and snippets.

@dwerbam
Created November 27, 2025 19:00
Show Gist options
  • Select an option

  • Save dwerbam/a5308d9d3043de1941fc89828d537e3c to your computer and use it in GitHub Desktop.

Select an option

Save dwerbam/a5308d9d3043de1941fc89828d537e3c to your computer and use it in GitHub Desktop.
receiver
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Optical Receiver</title>
<style>
body {
background-color: #000;
color: #0f0;
font-family: 'Courier New', monospace;
margin: 0;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
/* --- Screens --- */
#start-screen, #success-screen {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
text-align: center;
z-index: 10;
background: #000;
}
#scan-screen {
display: none;
position: relative;
height: 100%;
width: 100%;
}
/* --- UI Elements --- */
h1 { margin: 0 0 20px 0; text-shadow: 0 0 10px #0f0; }
button {
background: #002200;
border: 2px solid #0f0;
color: #0f0;
padding: 20px 40px;
font-size: 1.2rem;
font-weight: bold;
border-radius: 5px;
cursor: pointer;
text-transform: uppercase;
box-shadow: 0 0 15px #0f0;
}
button:active { background: #0f0; color: #000; }
/* --- Video Feed --- */
video {
width: 100%;
height: 100%;
object-fit: cover; /* Fills screen */
}
/* --- Overlay Stats --- */
#overlay {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.8);
padding: 15px;
border: 1px solid #0f0;
border-radius: 10px;
text-align: center;
width: 80%;
}
#progress-bar {
width: 100%;
height: 10px;
background: #333;
margin-top: 10px;
border-radius: 5px;
overflow: hidden;
}
#progress-fill {
height: 100%;
width: 0%;
background: #0f0;
transition: width 0.2s;
}
/* --- Success Message --- */
.success-text { font-size: 1.5rem; margin-bottom: 20px; color: #fff; }
.data-preview {
color: #888;
font-size: 0.8rem;
max-width: 80%;
word-break: break-all;
margin-bottom: 30px;
}
</style>
</head>
<body>
<!-- 1. START SCREEN -->
<div id="start-screen">
<button onclick="startCamera()">read</button>
<p style="color:#555; margin-top:20px; font-size: 0.8rem;">Ensure HTTPS or Localhost</p>
</div>
<!-- 2. SCANNING SCREEN -->
<div id="scan-screen">
<video id="video-feed" playsinline autoplay muted></video>
<div id="overlay">
<div id="status-text">WAITING FOR SIGNAL...</div>
<div id="progress-bar"><div id="progress-fill"></div></div>
</div>
<!-- Back Button -->
<button style="position:absolute; top:20px; right:20px; padding:10px; font-size:0.8rem; box-shadow:none;" onclick="stopCamera()">X</button>
</div>
<!-- 3. SUCCESS SCREEN -->
<div id="success-screen" style="display:none;">
<h1 style="color: #fff;">COMPLETE</h1>
<div class="success-text">Data Copied to Clipboard</div>
<div class="data-preview" id="final-data">...</div>
<button onclick="location.reload()">SCAN AGAIN</button>
</div>
<script>
let detector = null;
let scanInterval = null;
let receivedChunks = {};
let totalChunks = 0;
let videoStream = null;
// Check compatibility immediately
if (!('BarcodeDetector' in window)) {
alert("Your browser does not support native BarcodeDetector.\nPlease use Chrome on Android/Desktop.");
} else {
detector = new BarcodeDetector({ formats: ['qr_code'] });
}
async function startCamera() {
try {
// Request back camera
videoStream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: { exact: "environment" } }
}).catch(e => {
// Fallback if 'exact environment' fails (e.g. laptop)
return navigator.mediaDevices.getUserMedia({ video: true });
});
const video = document.getElementById('video-feed');
video.srcObject = videoStream;
// Switch UI
document.getElementById('start-screen').style.display = 'none';
document.getElementById('scan-screen').style.display = 'block';
// Reset State
receivedChunks = {};
totalChunks = 0;
updateUI("Looking for Stream...", 0);
// Start Detection Loop (Every 50ms for speed)
scanInterval = setInterval(scanFrame, 50);
} catch (err) {
alert("Camera Error: " + err.message);
}
}
function stopCamera() {
clearInterval(scanInterval);
if (videoStream) {
videoStream.getTracks().forEach(track => track.stop());
}
location.reload();
}
async function scanFrame() {
const video = document.getElementById('video-feed');
// Only scan if video is ready
if (video.readyState < 2) return;
try {
const barcodes = await detector.detect(video);
if (barcodes.length > 0) {
for (const barcode of barcodes) {
processQR(barcode.rawValue);
}
}
} catch (e) {
console.error(e);
}
}
function processQR(data) {
// Protocol: INDEX ||| TOTAL ||| DATA
const parts = data.split("|||", 3);
if (parts.length < 3) return;
const index = parseInt(parts[0]);
const total = parseInt(parts[1]);
const content = data.substring(parts[0].length + parts[1].length + 6); // Robust substring
// If new packet found
if (!receivedChunks[index]) {
receivedChunks[index] = content;
totalChunks = total;
// Update UI
const count = Object.keys(receivedChunks).length;
const percent = (count / total) * 100;
updateUI(`Receiving: ${count} / ${total}`, percent);
// Check Completion
if (count === total) {
finishTransfer();
}
}
}
function updateUI(text, percent) {
document.getElementById('status-text').innerText = text;
document.getElementById('progress-fill').style.width = percent + "%";
}
async function finishTransfer() {
clearInterval(scanInterval);
// Reassemble
let fullText = "";
for (let i = 1; i <= totalChunks; i++) {
fullText += receivedChunks[i];
}
// Copy to Clipboard
try {
await navigator.clipboard.writeText(fullText);
} catch (err) {
alert("Auto-copy failed. Please manually copy the text.");
}
// Show Success Screen
document.getElementById('scan-screen').style.display = 'none';
document.getElementById('success-screen').style.display = 'flex';
// Preview (Truncated)
document.getElementById('final-data').innerText = fullText.length > 50
? fullText.substring(0, 50) + "..."
: fullText;
// Stop Camera Hardware
if (videoStream) {
videoStream.getTracks().forEach(track => track.stop());
}
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment