ericjeon's picture
Manual changes saved
c3216d4 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ScribbleSense - Draw and Recognize Text</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="https://cdn.jsdelivr.net/npm/tesseract.js@4/dist/tesseract.min.js"></script>
</head>
<body class="bg-gradient-to-br from-indigo-100 to-purple-50 min-h-screen flex flex-col items-center justify-center p-4">
<div class="max-w-4xl w-full mx-auto text-center">
<h1 class="text-4xl md:text-5xl font-bold text-indigo-800 mb-2">ScribbleSense ✍️</h1>
<p class="text-lg text-indigo-600 mb-8">Draw anything and watch it transform into text!</p>
<div class="bg-white rounded-xl shadow-xl p-6 mb-8">
<div class="relative">
<canvas id="drawingCanvas" class="border-2 border-gray-300 rounded-lg w-full h-64 md:h-96 touch-none bg-white"></canvas>
<div id="clearBtn" class="absolute top-4 right-4 p-2 bg-white rounded-full shadow-md cursor-pointer hover:bg-gray-100 transition">
<i data-feather="x" class="text-gray-600"></i>
</div>
</div>
</div>
<div id="resultContainer" class="bg-white rounded-xl shadow-xl p-6 hidden">
<h2 class="text-2xl font-semibold text-indigo-700 mb-4">Recognized Text</h2>
<div id="resultText" class="text-3xl md:text-4xl font-mono p-4 bg-gray-50 rounded-lg min-h-20 break-all">
<!-- OCR result will appear here -->
</div>
<button id="copyBtn" class="mt-4 px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition flex items-center justify-center gap-2 mx-auto">
<i data-feather="copy" class="w-5 h-5"></i> Copy Text
</button>
</div>
<div class="mt-8 text-sm text-gray-500">
<p>Draw with left mouse button pressed. The system will automatically recognize your writing after a pause.</p>
<p class="mt-1">Supports: Roman alphabets, numbers, emojis, Chinese, Hangul, and more!</p>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
feather.replace();
const canvas = document.getElementById('drawingCanvas');
const ctx = canvas.getContext('2d');
const resultContainer = document.getElementById('resultContainer');
const resultText = document.getElementById('resultText');
const clearBtn = document.getElementById('clearBtn');
const copyBtn = document.getElementById('copyBtn');
// Set canvas size
function resizeCanvas() {
const containerWidth = canvas.parentElement.clientWidth;
canvas.width = containerWidth - 32; // accounting for padding
canvas.height = Math.min(window.innerHeight * 0.6, 600);
ctx.lineWidth = 4;
ctx.lineCap = 'round';
ctx.strokeStyle = '#4f46e5'; // indigo-600
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
// Drawing variables
let isDrawing = false;
let lastX = 0;
let lastY = 0;
let inactivityTimer;
let lastDrawTime = 0;
// Drawing functions
function startDrawing(e) {
isDrawing = true;
[lastX, lastY] = getPosition(e);
clearTimeout(inactivityTimer);
}
function draw(e) {
if (!isDrawing) return;
const [x, y] = getPosition(e);
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(x, y);
ctx.stroke();
[lastX, lastY] = [x, y];
lastDrawTime = Date.now();
// Reset inactivity timer
clearTimeout(inactivityTimer);
inactivityTimer = setTimeout(performOCR, 500);
}
function stopDrawing() {
isDrawing = false;
}
function getPosition(e) {
let x, y;
if (e.type.includes('touch')) {
const rect = canvas.getBoundingClientRect();
x = e.touches[0].clientX - rect.left;
y = e.touches[0].clientY - rect.top;
} else {
x = e.offsetX;
y = e.offsetY;
}
return [x, y];
}
// OCR function
function performOCR() {
if (Date.now() - lastDrawTime < 2) return;
Tesseract.recognize(
canvas,
'eng+chi_sim+chi_tra+kor+jpn+equ',
{
logger: m => console.log(m)
}
).then(({ data: { text } }) => {
resultText.textContent = text.trim() || "Couldn't recognize any text";
resultContainer.classList.remove('hidden');
// Scroll to result if needed
resultContainer.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
});
}
// Clear canvas
function clearCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
resultContainer.classList.add('hidden');
clearTimeout(inactivityTimer);
}
// Copy text function
function copyText() {
navigator.clipboard.writeText(resultText.textContent).then(() => {
const originalText = copyBtn.innerHTML;
copyBtn.innerHTML = '<i data-feather="check" class="w-5 h-5"></i> Copied!';
setTimeout(() => {
copyBtn.innerHTML = originalText;
feather.replace();
}, 2000);
});
}
// Event listeners
canvas.addEventListener('mousedown', startDrawing);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', stopDrawing);
canvas.addEventListener('mouseout', stopDrawing);
// Touch support
canvas.addEventListener('touchstart', (e) => {
e.preventDefault();
startDrawing(e);
});
canvas.addEventListener('touchmove', (e) => {
e.preventDefault();
draw(e);
});
canvas.addEventListener('touchend', stopDrawing);
clearBtn.addEventListener('click', clearCanvas);
copyBtn.addEventListener('click', copyText);
// Instructions tooltip
setTimeout(() => {
const tooltip = document.createElement('div');
tooltip.className = 'absolute bg-indigo-600 text-white px-4 py-2 rounded-lg shadow-lg z-10 animate-bounce';
tooltip.style.top = '20px';
tooltip.style.left = '50%';
tooltip.style.transform = 'translateX(-50%)';
tooltip.textContent = 'Draw here with your mouse or finger!';
canvas.parentElement.appendChild(tooltip);
setTimeout(() => {
tooltip.remove();
}, 3000);
}, 1000);
});
</script>
</body>
</html>