@liveloop · 5/17/2026, 12:03:28 PM
Initial version — all lines are new.
+<style>+ html,body{height:100%;margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;background:#fef3c7;overflow:hidden;}+ .wrap{display:flex;flex-direction:column;height:100%;box-sizing:border-box;padding:.5rem;gap:.4rem;}+ h1{margin:0;font-size:1rem;text-align:center;color:#92400e;}+ #board{flex:1;min-height:0;border-radius:1rem;overflow:hidden;background:#fff;box-shadow:0 6px 18px rgba(0,0,0,.18);}+ canvas{width:100%;height:100%;display:block;touch-action:none;cursor:crosshair;}+ .colors{display:flex;flex-wrap:wrap;gap:.3rem;justify-content:center;}+ .sw{width:2rem;height:2rem;border-radius:50%;border:3px solid #fff;box-shadow:0 1px 3px rgba(0,0,0,.35);cursor:pointer;touch-action:manipulation;padding:0;}+ .sw.on{outline:3px solid #1f2937;outline-offset:1px;}+ .tools{display:flex;gap:.35rem;justify-content:center;align-items:center;flex-wrap:wrap;}+ .tool{font:inherit;font-size:.82rem;font-weight:700;border:0;border-radius:9999px;padding:.5rem .85rem;background:#fff;color:#1f2937;box-shadow:0 2px 5px rgba(0,0,0,.2);cursor:pointer;touch-action:manipulation;}+ .tool.on{background:#1f2937;color:#fff;}+ .brush{width:2.4rem;height:2.4rem;border-radius:50%;border:0;background:#fff;box-shadow:0 2px 5px rgba(0,0,0,.2);cursor:pointer;touch-action:manipulation;display:flex;align-items:center;justify-content:center;}+ .brush.on{background:#1f2937;}+ .dot{border-radius:50%;background:#1f2937;display:block;}+ .brush.on .dot{background:#fff;}+</style>+<div class="wrap">+ <h1>🎨 Doodle Pad</h1>+ <div id="board"><canvas id="cv"></canvas></div>+ <div class="colors" id="colors"></div>+ <div class="tools">+ <button class="brush on" data-size="6" type="button"><span class="dot" style="width:7px;height:7px"></span></button>+ <button class="brush" data-size="16" type="button"><span class="dot" style="width:14px;height:14px"></span></button>+ <button class="brush" data-size="30" type="button"><span class="dot" style="width:22px;height:22px"></span></button>+ <button class="tool" id="eraser" type="button">🧽 Eraser</button>+ <button class="tool" id="undo" type="button">↩️ Undo</button>+ <button class="tool" id="clear" type="button">🗑️ Clear</button>+ </div>+</div>+<script>+(function(){+ var COLORS=['#ef4444','#f97316','#facc15','#22c55e','#06b6d4','#3b82f6','#a855f7','#ec4899','#92400e','#1f2937','#ffffff'];+ var cv=document.getElementById('cv'), ctx=cv.getContext('2d');+ var colorsEl=document.getElementById('colors');+ var color='#ef4444', size=6, erasing=false;+ var strokes=[], curStroke=null, W=0, H=0;++ function onTap(el,fn){+ var sx=0,sy=0,armed=false;+ el.addEventListener('pointerdown',function(e){ armed=true;sx=e.clientX;sy=e.clientY; });+ el.addEventListener('pointercancel',function(){ armed=false; });+ el.addEventListener('pointerup',function(e){+ if(!armed) return; armed=false;+ if(Math.abs(e.clientX-sx)<14&&Math.abs(e.clientY-sy)<14) fn();+ });+ }+ function fit(){+ var r=cv.getBoundingClientRect();+ if(r.width<=0||r.height<=0) return;+ var dpr=Math.min(window.devicePixelRatio||1,2);+ W=r.width; H=r.height;+ cv.width=Math.round(W*dpr); cv.height=Math.round(H*dpr);+ ctx.setTransform(dpr,0,0,dpr,0,0);+ redraw();+ }+ function redraw(){+ ctx.clearRect(0,0,W,H);+ ctx.fillStyle='#ffffff'; ctx.fillRect(0,0,W,H);+ ctx.lineCap='round'; ctx.lineJoin='round';+ for(var i=0;i<strokes.length;i++) drawStroke(strokes[i]);+ }+ function drawStroke(s){+ if(!s.pts.length) return;+ ctx.strokeStyle=s.color; ctx.lineWidth=s.size;+ ctx.lineCap='round'; ctx.lineJoin='round';+ ctx.beginPath();+ ctx.moveTo(s.pts[0].x*W,s.pts[0].y*H);+ for(var i=1;i<s.pts.length;i++) ctx.lineTo(s.pts[i].x*W,s.pts[i].y*H);+ if(s.pts.length===1) ctx.lineTo(s.pts[0].x*W+0.1,s.pts[0].y*H+0.1);+ ctx.stroke();+ }+ function pt(e){+ var r=cv.getBoundingClientRect();+ return { x:(e.clientX-r.left)/r.width, y:(e.clientY-r.top)/r.height };+ }++ var drawing=false;+ cv.addEventListener('pointerdown',function(e){+ drawing=true;+ try{ cv.setPointerCapture(e.pointerId); }catch(_){}+ curStroke={ color: erasing?'#ffffff':color, size:size, pts:[pt(e)] };+ strokes.push(curStroke);+ drawStroke(curStroke);+ e.preventDefault();+ });+ cv.addEventListener('pointermove',function(e){+ if(!drawing||!curStroke) return;+ curStroke.pts.push(pt(e));+ var n=curStroke.pts.length;+ ctx.strokeStyle=curStroke.color; ctx.lineWidth=curStroke.size;+ ctx.lineCap='round'; ctx.lineJoin='round';+ ctx.beginPath();+ ctx.moveTo(curStroke.pts[n-2].x*W,curStroke.pts[n-2].y*H);+ ctx.lineTo(curStroke.pts[n-1].x*W,curStroke.pts[n-1].y*H);+ ctx.stroke();+ e.preventDefault();+ });+ function endStroke(e){+ drawing=false; curStroke=null;+ try{ cv.releasePointerCapture(e.pointerId); }catch(_){}+ }+ cv.addEventListener('pointerup',endStroke);+ cv.addEventListener('pointercancel',endStroke);++ COLORS.forEach(function(c){+ var b=document.createElement('button');+ b.type='button'; b.className='sw'; b.style.background=c;+ if(c==='#ef4444') b.classList.add('on');+ onTap(b,function(){+ color=c; erasing=false;+ document.getElementById('eraser').classList.remove('on');+ var sws=colorsEl.querySelectorAll('.sw');+ for(var k=0;k<sws.length;k++) sws[k].classList.toggle('on',sws[k]===b);+ });+ colorsEl.appendChild(b);+ });++ var brushes=document.querySelectorAll('.brush');+ Array.prototype.forEach.call(brushes,function(b){+ onTap(b,function(){+ size=parseInt(b.getAttribute('data-size'),10);+ Array.prototype.forEach.call(brushes,function(o){ o.classList.toggle('on',o===b); });+ });+ });+ var eraserBtn=document.getElementById('eraser');+ onTap(eraserBtn,function(){+ erasing=!erasing;+ eraserBtn.classList.toggle('on',erasing);+ });+ onTap(document.getElementById('undo'),function(){ strokes.pop(); redraw(); });+ onTap(document.getElementById('clear'),function(){ strokes=[]; redraw(); });++ window.addEventListener('resize',fit);+ fit();+ requestAnimationFrame(fit);+})();+</script>