Productivity
HANDWRITING
A digital notepad for handwriting and drawing. Write with your mouse, stylus, or touchscreen.
Tip: Use this as a scratchpad for quick notes, sketches, or signatures. Works best with a touchscreen or drawing tablet.
Developer Reference
Core Algorithm & Standalone Script
Standalone, zero-dependency JavaScript implementation powering this tool. Free to inspect, copy, and build upon.
const canvas = document.getElementById('noteCanvas');
const ctx = canvas.getContext('2d');
const linesCanvas = document.getElementById('linesCanvas');
const linesCtx = linesCanvas.getContext('2d');
let penColor = '#000000', penWidth = 3, currentTool = 'pen';
let drawing = false, lastX, lastY;
let strokes = [], currentStroke = [];
let history = [], historyIdx = -1;
function initCanvas() {
const w = canvas.parentElement.clientWidth;
const h = 400;
canvas.width = w; canvas.height = h;
linesCanvas.width = w; linesCanvas.height = h;
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, w, h);
drawLines();
saveState();
}
function drawLines() {
linesCtx.clearRect(0, 0, linesCanvas.width, linesCanvas.height);
linesCtx.strokeStyle = '#cbd5e1';
linesCtx.lineWidth = 1;
for (let y = 36; y < linesCanvas.height; y += 32) {
linesCtx.beginPath();
linesCtx.moveTo(0, y);
linesCtx.lineTo(linesCanvas.width, y);
linesCtx.stroke();
}
}
function getPos(e) {
const r = canvas.getBoundingClientRect();
const x = (e.clientX || (e.touches && e.touches[0].clientX)) - r.left;
const y = (e.clientY || (e.touches && e.touches[0].clientY)) - r.top;
return { x, y };
}
function startDraw(e) {
drawing = true;
const p = getPos(e);
lastX = p.x; lastY = p.y;
ctx.beginPath();
ctx.moveTo(p.x, p.y);
}
function moveDraw(e) {
if (!drawing) return;
const p = getPos(e);
ctx.strokeStyle = currentTool === 'eraser' ? '#ffffff' : penColor;
ctx.lineWidth = currentTool === 'eraser' ? penWidth * 4 : penWidth;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(p.x, p.y);
ctx.stroke();
lastX = p.x; lastY = p.y;
}
function endDraw() {
if (drawing) { drawing = false; saveState(); }
}
canvas.addEventListener('mousedown', startDraw);
canvas.addEventListener('mousemove', moveDraw);
canvas.addEventListener('mouseup', endDraw);
canvas.addEventListener('mouseleave', endDraw);
canvas.addEventListener('touchstart', e => { e.preventDefault(); startDraw(e); }, { passive: false });
canvas.addEventListener('touchmove', e => { e.preventDefault(); moveDraw(e); }, { passive: false });
canvas.addEventListener('touchend', endDraw);
function setTool(t) {
currentTool = t;
document.querySelectorAll('.tb-btn[data-tool]').forEach(b => b.classList.toggle('active', b.dataset.tool === t));
}
function setColor(c) {
penColor = c;
document.querySelectorAll('.tb-color').forEach(b => b.classList.toggle('active', b.dataset.color === c));
}
function setWidth(w) {
penWidth = w;
}
function saveState() {
historyIdx++;
history = history.slice(0, historyIdx);
history.push(ctx.getImageData(0, 0, canvas.width, canvas.height));
if (history.length > 40) { history.shift(); historyIdx--; }
}
function undo() {
if (historyIdx > 0) { historyIdx--; ctx.putImageData(history[historyIdx], 0, 0); }
}
function clearCanvas() {
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
saveState();
}
function exportPNG() {
const a = document.createElement('a');
a.download = 'handwriting.png';
a.href = canvas.toDataURL('image/png');
a.click();
}
function exportWithNotes() {
const notes = document.getElementById('textNotes').value;
const exportCanvas = document.createElement('canvas');
exportCanvas.width = canvas.width;
const textLines = notes ? notes.split('\n') : [];
const textHeight = textLines.length > 0 ? textLines.length * 22 + 40 : 0;
exportCanvas.height = canvas.height + textHeight;
const ectx = exportCanvas.getContext('2d');
ectx.fillStyle = '#ffffff';
ectx.fillRect(0, 0, exportCanvas.width, exportCanvas.height);
ectx.drawImage(canvas, 0, 0);
if (textLines.length > 0) {
ectx.fillStyle = '#f8f9fa';
ectx.fillRect(0, canvas.height, exportCanvas.width, textHeight);
ectx.fillStyle = '#1a1a1a';
ectx.font = '14px monospace';
textLines.forEach((line, i) => {
ectx.fillText(line, 16, canvas.height + 24 + i * 22);
});
}
const a = document.createElement('a');
a.download = 'handwriting-notes.png';
a.href = exportCanvas.toDataURL('image/png');
a.click();
}
window.addEventListener('resize', () => {
const img = ctx.getImageData(0, 0, canvas.width, canvas.height);
const w = canvas.parentElement.clientWidth;
canvas.width = w; linesCanvas.width = w;
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.putImageData(img, 0, 0);
drawLines();
});
initCanvas();