Last active
February 2, 2026 17:30
-
-
Save HalanoSiblee/8f7535af24e130de7e6277840d0073e8 to your computer and use it in GitHub Desktop.
blazing fast diagram & drawing tool.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!-- | |
| HalanoSiblee // Google Gemini | |
| ( C ) 2026 | |
| MIT | |
| --> | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>MothDrawJs</title> | |
| <style> | |
| body { margin: 0; overflow: hidden; background: #000; font-family: 'Segoe UI', sans-serif; } | |
| canvas { position: absolute; top: 0; left: 0; outline: none; cursor: crosshair; } | |
| #ui { | |
| position: fixed; top: 15px; left: 15px; z-index: 10; | |
| pointer-events: none; color: #fff; font-size: 13px; | |
| letter-spacing: 1px; line-height: 1.6; | |
| } | |
| .key { color: #555; font-weight: bold; margin-right: 5px; } | |
| #colorInd { font-weight: bold; text-shadow: 0 0 10px currentColor; font-size: 16px; } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="ui"> | |
| Color: <span id="colorInd">WHITE</span> | MODE: <span id="modeInd">PEN</span><br> | |
| <span class="key">[0-9]</span> Colors | <span class="key">[T,C,R,P,X]</span> Modes | <span class="key">[S]</span> Save | <span class="key">[SPACE]</span> Clear | |
| </div> | |
| <canvas id="main"></canvas> | |
| <canvas id="buffer"></canvas> | |
| <script> | |
| const canvas = document.getElementById('main'); | |
| const buffer = document.getElementById('buffer'); | |
| const ctx = canvas.getContext('2d', { alpha: false }); | |
| const bCtx = buffer.getContext('2d'); | |
| const colorInd = document.getElementById('colorInd'); | |
| const modeInd = document.getElementById('modeInd'); | |
| let drawing = false; | |
| let x1, y1, lx, ly; | |
| let mode = 'p'; | |
| let textBuffer = ""; | |
| let fontSize = 30; | |
| const colors = ['#FFFFFF', '#FF0000', '#FF7F00', '#FFFF00', '#00FF00', '#0000FF', '#4B0082', '#8B00FF', '#FF69B4', '#FF1493']; | |
| const colorNames = ['WHITE', 'RED', 'ORANGE', 'YELLOW', 'GREEN', 'BLUE', 'INDIGO', 'VIOLET', 'PINK', 'DEEP PINK']; | |
| let activeColor = colors[0]; | |
| const setSize = () => { | |
| const w = window.innerWidth, h = window.innerHeight; | |
| const temp = ctx.getImageData(0, 0, canvas.width, canvas.height); | |
| canvas.width = w; | |
| canvas.height = h; | |
| buffer.width = w; | |
| buffer.height = h; | |
| ctx.fillStyle = '#000'; | |
| ctx.fillRect(0, 0, w, h); | |
| ctx.putImageData(temp, 0, 0); | |
| ctx.lineCap = bCtx.lineCap = 'round'; | |
| ctx.lineWidth = bCtx.lineWidth = 2; | |
| updateFont(); | |
| }; | |
| const updateFont = () => { | |
| const fontStr = `bold ${fontSize}px Arial`; | |
| ctx.font = bCtx.font = fontStr; | |
| }; | |
| window.onresize = setSize; | |
| setSize(); | |
| window.onkeydown = e => { | |
| const k = e.key.toLowerCase(); | |
| if (k >= '0' && k <= '9') { | |
| activeColor = colors[k]; | |
| colorInd.innerText = colorNames[k]; | |
| colorInd.style.color = activeColor; | |
| } | |
| if (['t','c','r','p','x'].includes(k)) { | |
| mode = k; | |
| modeInd.innerText = k === 'p' ? 'PEN' : k === 'x' ? 'TEXT' : k.toUpperCase(); | |
| // Prompt immediately when switching to text to avoid click-loop | |
| if (k === 'x') { | |
| textBuffer = prompt("Enter text:", textBuffer) || "TEXT"; | |
| fontSize = parseInt(prompt("Enter font size:", fontSize)) || 30; | |
| updateFont(); | |
| } | |
| } | |
| if (e.code === 'Space') ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| if (k === 's') { | |
| const link = document.createElement('a'); | |
| link.download = `mothdraw-${Date.now()}.png`; | |
| link.href = canvas.toDataURL(); | |
| link.click(); | |
| } | |
| }; | |
| buffer.onmousedown = e => { | |
| drawing = true; | |
| [x1, y1] = [e.clientX, e.clientY]; | |
| [lx, ly] = [x1, y1]; | |
| }; | |
| window.onmousemove = e => { | |
| if (!drawing) return; | |
| const [x2, y2] = [e.clientX, e.clientY]; | |
| if (mode === 'p' && !e.shiftKey) { | |
| ctx.beginPath(); | |
| ctx.strokeStyle = activeColor; | |
| ctx.moveTo(lx, ly); | |
| ctx.lineTo(x2, y2); | |
| ctx.stroke(); | |
| [lx, ly] = [x2, y2]; | |
| } else { | |
| bCtx.clearRect(0, 0, buffer.width, buffer.height); | |
| bCtx.beginPath(); | |
| bCtx.strokeStyle = bCtx.fillStyle = activeColor; | |
| const m = e.shiftKey ? 'line' : mode; | |
| if (m === 'line') { | |
| bCtx.moveTo(x1, y1); bCtx.lineTo(x2, y2); bCtx.stroke(); | |
| } else if (m === 'r') { | |
| bCtx.strokeRect(x1, y1, x2 - x1, y2 - y1); | |
| } else if (m === 'c') { | |
| bCtx.arc(x1, y1, Math.hypot(x2 - x1, y2 - y1), 0, 7); bCtx.stroke(); | |
| } else if (m === 't') { | |
| bCtx.moveTo(x1 + (x2 - x1)/2, y1); bCtx.lineTo(x1, y2); bCtx.lineTo(x2, y2); bCtx.closePath(); bCtx.stroke(); | |
| } else if (m === 'x') { | |
| bCtx.fillText(textBuffer, x2, y2); | |
| } | |
| } | |
| }; | |
| window.onmouseup = e => { | |
| if (!drawing) return; | |
| if (mode !== 'p' || e.shiftKey) { | |
| ctx.drawImage(buffer, 0, 0); | |
| bCtx.clearRect(0, 0, buffer.width, buffer.height); | |
| } | |
| drawing = false; | |
| }; | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment