Skip to content

Instantly share code, notes, and snippets.

@HalanoSiblee
Last active February 2, 2026 17:30
Show Gist options
  • Select an option

  • Save HalanoSiblee/8f7535af24e130de7e6277840d0073e8 to your computer and use it in GitHub Desktop.

Select an option

Save HalanoSiblee/8f7535af24e130de7e6277840d0073e8 to your computer and use it in GitHub Desktop.
blazing fast diagram & drawing tool.
<!--
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