pdq-wasm
Version:
WebAssembly bindings for Meta's PDQ perceptual image hashing algorithm
156 lines (129 loc) • 4.07 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PDQ Web Worker Example</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
max-width: 800px;
margin: 50px auto;
padding: 20px;
}
.container {
border: 2px dashed #ccc;
border-radius: 8px;
padding: 40px;
text-align: center;
background: #f9f9f9;
}
input[type="file"] {
margin: 20px 0;
}
#results {
margin-top: 30px;
text-align: left;
}
.hash-result {
background: white;
padding: 15px;
margin: 10px 0;
border-radius: 4px;
border-left: 4px solid #4CAF50;
}
.error {
border-left-color: #f44336;
}
.hash {
font-family: 'Courier New', monospace;
font-size: 12px;
word-break: break-all;
color: #333;
margin-top: 5px;
}
.status {
padding: 10px;
margin: 10px 0;
border-radius: 4px;
background: #e3f2fd;
color: #1976d2;
}
</style>
</head>
<body>
<h1>PDQ Web Worker Example</h1>
<p>This example demonstrates using pdq-wasm in a Web Worker for non-blocking image hashing.</p>
<div class="container">
<h2>Select Images to Hash</h2>
<input type="file" id="fileInput" accept="image/*" multiple>
<p>Select one or more images. They will be hashed in a Web Worker without blocking the UI.</p>
</div>
<div id="status"></div>
<div id="results"></div>
<script>
const fileInput = document.getElementById('fileInput');
const statusDiv = document.getElementById('status');
const resultsDiv = document.getElementById('results');
// Create a Web Worker
const worker = new Worker('pdq-worker.js');
// Handle worker messages
worker.onmessage = (event) => {
const { type, hash, filename, error } = event.data;
switch (type) {
case 'ready':
statusDiv.innerHTML = '<div class="status">✓ Worker ready</div>';
break;
case 'hash-result': {
const resultDiv = document.createElement('div');
resultDiv.className = 'hash-result';
const strong = document.createElement('strong');
strong.textContent = filename;
const hashDiv = document.createElement('div');
hashDiv.className = 'hash';
hashDiv.textContent = hash;
resultDiv.appendChild(strong);
resultDiv.appendChild(hashDiv);
resultsDiv.appendChild(resultDiv);
break;
}
case 'error': {
const errorDiv = document.createElement('div');
errorDiv.className = 'hash-result error';
const strong = document.createElement('strong');
strong.textContent = `Error${filename ? ': ' + filename : ''}`;
const errorMsgDiv = document.createElement('div');
errorMsgDiv.textContent = error;
errorDiv.appendChild(strong);
errorDiv.appendChild(errorMsgDiv);
resultsDiv.appendChild(errorDiv);
break;
}
}
};
worker.onerror = (error) => {
console.error('Worker error:', error);
statusDiv.innerHTML = '<div class="status" style="background: #ffebee; color: #c62828;">✗ Worker error: ' + error.message + '</div>';
};
// Initialize the worker first
worker.postMessage({ type: 'init' });
// Handle file selection
fileInput.addEventListener('change', async (event) => {
const files = event.target.files;
if (files.length === 0) return;
resultsDiv.innerHTML = '';
statusDiv.innerHTML = `<div class="status">Processing ${files.length} image(s)...</div>`;
// Send each file to the worker
for (const file of files) {
worker.postMessage({
type: 'hash',
data: {
file,
filename: file.name
}
});
}
});
</script>
</body>
</html>