File size: 7,183 Bytes
a566fb0 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>🕵️♂️ Secret Agent Motion Detector</title>
<style>
/* CSS: Making it look cool */
body {
background-color: #1a1a1a; /* Dark spy background */
color: #00ff41; /* Hacker green text */
font-family: 'Courier New', Courier, monospace; /* Typewriter font */
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
margin: 0;
padding: 20px;
}
h1 {
text-shadow: 0 0 10px #00ff41;
margin-bottom: 10px;
}
.instructions {
background: #222;
padding: 15px;
border-radius: 10px;
border: 1px solid #444;
max-width: 600px;
text-align: center;
margin-bottom: 20px;
color: #ddd;
}
/* The canvas where the magic happens */
#canvasFinal {
border: 4px solid #00ff41;
border-radius: 8px;
box-shadow: 0 0 20px rgba(0, 255, 65, 0.3);
background-color: #000;
max-width: 100%;
display: none; /* Hidden until camera starts */
}
/* Hide the raw video stream and processing canvas */
#camStream, #canvasHidden {
display: none;
}
button {
background-color: #00ff41;
color: #000;
border: none;
padding: 15px 30px;
font-size: 1.2rem;
font-weight: bold;
font-family: inherit;
cursor: pointer;
border-radius: 5px;
margin-bottom: 20px;
transition: all 0.3s;
}
button:hover {
background-color: #fff;
box-shadow: 0 0 15px #fff;
}
.status {
margin-top: 10px;
font-size: 0.9rem;
color: #888;
}
</style>
</head>
<body>
<button id="startBtn" onclick="startSpyCamera()">🚀 Activate Movement Detection Mode</button>
<p class="status" id="statusText">Waiting for activation...</p>
<!-- These are the HTML elements that handle the video -->
<video id="camStream" playsinline autoplay></video>
<canvas id="canvasHidden"></canvas>
<canvas id="canvasFinal"></canvas>
<script>
// JAVASCRIPT: The Brains of the Operation
// 1. Grab our HTML elements so we can control them
const video = document.getElementById('camStream');
const hiddenCanvas = document.getElementById('canvasHidden');
const finalCanvas = document.getElementById('canvasFinal');
const startBtn = document.getElementById('startBtn');
const statusText = document.getElementById('statusText');
// Contexts are like the "paintbrushes" for the canvas
const hiddenCtx = hiddenCanvas.getContext('2d');
const finalCtx = finalCanvas.getContext('2d');
// We need a place to store the "previous" frame to compare against
let previousFrameData = null;
async function startSpyCamera() {
try {
// 2. Ask the browser for permission to use the camera
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
// Connect the camera stream to our hidden video element
video.srcObject = stream;
// Update the UI
statusText.innerText = "System Active. Scanning for movement...";
startBtn.style.display = "none"; // Hide button
finalCanvas.style.display = "block"; // Show screen
// Start the loop!
requestAnimationFrame(processVideo);
} catch (err) {
// If they say no or don't have a camera
statusText.innerText = "⚠️ Error: Could not access camera. Please allow permission.";
console.error(err);
}
}
function processVideo() {
// Check if the video is ready and playing
if (video.readyState === video.HAVE_ENOUGH_DATA) {
// Set canvas sizes to match the video size
const width = video.videoWidth;
const height = video.videoHeight;
hiddenCanvas.width = width;
hiddenCanvas.height = height;
finalCanvas.width = width;
finalCanvas.height = height;
// 3. Draw the current video frame onto the hidden canvas
hiddenCtx.drawImage(video, 0, 0, width, height);
// 4. Get the pixel data (the raw numbers for every color)
const currentFrame = hiddenCtx.getImageData(0, 0, width, height);
const currentData = currentFrame.data;
// Create a new image for the result
const outputImage = finalCtx.createImageData(width, height);
const outputData = outputImage.data;
// 5. THE ALGORITHM: Compare this frame to the previous one
if (previousFrameData) {
const prevData = previousFrameData.data;
// Loop through every pixel (pixels are groups of 4: Red, Green, Blue, Alpha)
for (let i = 0; i < currentData.length; i += 4) {
// This math calculates the difference between old and new
// If pixels are the same, it turns GREY.
// If they are different, it creates high contrast colors.
outputData[i] = 0.5 * (255 - currentData[i]) + 0.5 * prevData[i]; // Red
outputData[i+1] = 0.5 * (255 - currentData[i+1]) + 0.5 * prevData[i+1]; // Green
outputData[i+2] = 0.5 * (255 - currentData[i+2]) + 0.5 * prevData[i+2]; // Blue
outputData[i+3] = 255; // Alpha (Opacity) is always 100%
}
// Draw the result to the main screen
finalCtx.putImageData(outputImage, 0, 0);
}
// 6. Save the current frame to be the "previous" frame for next time
// We clone it so we don't overwrite it immediately
previousFrameData = new ImageData(
new Uint8ClampedArray(currentFrame.data),
currentFrame.width,
currentFrame.height
);
}
// Repeat this function as fast as the screen can refresh
requestAnimationFrame(processVideo);
}
</script>
</body>
</html>
|