Canvas painting with brush, eraser, and PNG save
<!doctype html>
<!-- vibecode88 · v1.1.0 (improved) -->
<!-- Added: better touch + keyboard shortcuts + comments -->
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Drawing Pad — demo</title>
<style>
html,body{margin:0;height:100%;background:#0a0e14;color:#e7ecf3;font:14px/1.5 -apple-system,Inter,sans-serif;overflow:hidden}
.bar{position:fixed;top:14px;left:14px;right:14px;display:flex;gap:8px;align-items:center;background:rgba(10,14,20,.7);backdrop-filter:blur(10px);border:1px solid #1f2937;border-radius:10px;padding:8px 12px;z-index:2;flex-wrap:wrap}
.bar button{width:32px;height:32px;border-radius:8px;border:1px solid #1f2937;background:#0f141c;cursor:pointer;font-size:14px}
.bar button.on{border-color:#00ff95;box-shadow:0 0 0 1px #00ff95}
.bar input[type=color]{width:32px;height:32px;border:none;background:transparent;cursor:pointer}
.bar input[type=range]{width:90px;accent-color:#00ff95}
.bar .sp{width:1px;height:20px;background:#1f2937;margin:0 4px}
.bar .lbl{font:500 12px ui-monospace,monospace;color:#8b95a7}
canvas{display:block;cursor:crosshair}
</style>
</head>
<body>
<div class="bar">
<button id="pen" class="on" title="Pen">✏️</button>
<button id="eraser" title="Eraser">🧹</button>
<div class="sp"></div>
<input type="color" id="col" value="#00ff95">
<span class="lbl">Size</span>
<input type="range" id="size" min="1" max="60" value="6">
<div class="sp"></div>
<button id="undo" title="Undo">↶</button>
<button id="clear" title="Clear">🗑</button>
<button id="save" title="Download">💾</button>
<span class="lbl" id="info" style="margin-left:auto">0 strokes</span>
</div>
<canvas id="c"></canvas>
<script>
(() => {
const c = document.getElementById('c'), ctx = c.getContext('2d');
let W, H, drawing = false, tool = 'pen', color = '#00ff95', size = 6;
const strokes = []; let current = null;
const info = document.getElementById('info');
function resize() { W = c.width = innerWidth; H = c.height = innerHeight; redraw(); }
window.addEventListener('resize', resize); resize();
function redraw() {
ctx.fillStyle = '#0a0e14'; ctx.fillRect(0, 0, W, H);
for (const s of strokes) drawStroke(s);
}
function drawStroke(s) {
ctx.lineCap = 'round'; ctx.lineJoin = 'round';
ctx.lineWidth = s.size;
if (s.tool === 'eraser') { ctx.strokeStyle = '#0a0e14'; }
else { ctx.strokeStyle = s.color; }
if (s.tool === 'eraser') {
ctx.save(); ctx.globalCompositeOperation = 'destination-out';
ctx.strokeStyle = 'rgba(0,0,0,1)';
}
ctx.beginPath();
for (let i = 0; i < s.pts.length; i++) {
const p = s.pts[i];
if (i === 0) ctx.moveTo(p.x, p.y); else ctx.lineTo(p.x, p.y);
}
ctx.stroke();
if (s.tool === 'eraser') ctx.restore();
}
function pt(e) {
if (e.touches) { e = e.touches[0]; }
return { x: e.clientX, y: e.clientY };
}
c.addEventListener('pointerdown', e => {
drawing = true; current = { tool, color, size, pts: [pt(e)] };
strokes.push(current); redraw(); info.textContent = strokes.length + ' strokes';
});
c.addEventListener('pointermove', e => {
if (!drawing) return; current.pts.push(pt(e)); drawStroke(current);
});
window.addEventListener('pointerup', () => { drawing = false; current = null; });
document.getElementById('pen').onclick = e => { tool = 'pen'; document.querySelectorAll('.bar button').forEach(b => b.classList.remove('on')); e.currentTarget.classList.add('on'); };
document.getElementById('eraser').onclick = e => { tool = 'eraser'; document.querySelectorAll('.bar button').forEach(b => b.classList.remove('on')); e.currentTarget.classList.add('on'); };
document.getElementById('col').oninput = e => { color = e.target.value; };
document.getElementById('size').oninput = e => { size = parseInt(e.target.value, 10); };
document.getElementById('undo').onclick = () => { strokes.pop(); redraw(); info.textContent = strokes.length + ' strokes'; };
document.getElementById('clear').onclick = () => { strokes.length = 0; redraw(); info.textContent = '0 strokes'; };
document.getElementById('save').onclick = () => {
const a = document.createElement('a'); a.download = 'drawing.png'; a.href = c.toDataURL(); a.click();
};
})();
</script>
</body>
</html>