You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
blurSize = "4",
lineStrength = "3",
bgColor = "#133c76",
majorGridColor = "rgba(255, 255, 255, 0.2)",
minorGridColor = "rgba(255, 255, 255, 0.08)"
) {
const w = originalImg.width || originalImg.naturalWidth;
const h = originalImg.height || originalImg.naturalHeight;
// 1. Output Canvas setup
const canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
// Fill blueprint blue background
ctx.fillStyle = bgColor;
ctx.fillRect(0, 0, w, h);
// 2. Draw Blueprint Grid
const minorStep = Math.max(8, Math.min(w, h) * 0.015);
const majorStep = minorStep * 5;
ctx.strokeStyle = minorGridColor;
ctx.lineWidth = Math.max(0.5, w * 0.001);
ctx.beginPath();
for (let x = 0; x <= w; x += minorStep) { ctx.moveTo(x, 0); ctx.lineTo(x, h); }
for (let y = 0; y <= h; y += minorStep) { ctx.moveTo(0, y); ctx.lineTo(w, y); }
ctx.stroke();
ctx.strokeStyle = majorGridColor;
ctx.lineWidth = Math.max(1, w * 0.002);
ctx.beginPath();
for (let x = 0; x <= w; x += majorStep) { ctx.moveTo(x, 0); ctx.lineTo(x, h); }
for (let y = 0; y <= h; y += majorStep) { ctx.moveTo(0, y); ctx.lineTo(w, y); }
ctx.stroke();
// 3. Prepare an opaque base of the image to ensure consistent shading
const baseSrc = document.createElement('canvas');
baseSrc.width = w;
baseSrc.height = h;
const bsCtx = baseSrc.getContext('2d');
bsCtx.fillStyle = '#ffffff';
bsCtx.fillRect(0, 0, w, h);
bsCtx.drawImage(originalImg, 0, 0);
// 4. Sketch Generator Canvas
const sketchC = document.createElement('canvas');
sketchC.width = w;
sketchC.height = h;
const sCtx = sketchC.getContext('2d');
// Step A: Draw grayscale image
sCtx.filter = 'grayscale(1)';
sCtx.drawImage(baseSrc, 0, 0);
// Step B: Blend inverted blurred copy over it using color-dodge mapping
// This creates the "pencil sketch" look (dark edges, white flat surfaces)
sCtx.globalCompositeOperation = 'color-dodge';
sCtx.filter = `grayscale(1) invert(1) blur(${parseFloat(blurSize)}px)`;
sCtx.drawImage(baseSrc, 0, 0);
// Step C: Multiply to enhance the line darkness / intensity
const iterations = parseInt(lineStrength);
if (iterations > 1) {
// Ensure no recursive read/write issues in buggy browsers
const copyC = document.createElement('canvas');
copyC.width = w; copyC.height = h;
copyC.getContext('2d').drawImage(sketchC, 0, 0);
sCtx.globalCompositeOperation = 'multiply';
sCtx.filter = 'none';
for (let i = 1; i < iterations; i++) {
sCtx.drawImage(copyC, 0, 0);
}
}
// 5. Invert the dark lines into white lines
const sketchInvert = document.createElement('canvas');
sketchInvert.width = w;
sketchInvert.height = h;
const siCtx = sketchInvert.getContext('2d');
siCtx.filter = 'invert(1)';
siCtx.drawImage(sketchC, 0, 0);
// 6. Draw the sketch over the background using 'screen' to keep only the bright lines
ctx.globalCompositeOperation = 'screen';
ctx.drawImage(sketchInvert, 0, 0);
// 7. Add rough sketch paper noise texture
const noisePatSize = 250;
const noiseC = document.createElement('canvas');
noiseC.width = noisePatSize; noiseC.height = noisePatSize;
const nCtx = noiseC.getContext('2d');
const nData = nCtx.createImageData(noisePatSize, noisePatSize);
for (let i = 0; i < nData.data.length; i += 4) {
let val = Math.random() * 255;
nData.data[i] = val; // R
nData.data[i+1] = val; // G
nData.data[i+2] = val; // B
nData.data[i+3] = 12; // Very low alpha noise
}
nCtx.putImageData(nData, 0, 0);
ctx.globalCompositeOperation = 'screen';
ctx.fillStyle = ctx.createPattern(noiseC, 'repeat');
ctx.fillRect(0, 0, w, h);
// 8. Draw Drafting/Architectural Border to complete the Blueprint aesthetic
ctx.globalCompositeOperation = 'source-over';
const margin = Math.max(10, Math.min(40, w * 0.03, h * 0.03));
ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)';
ctx.lineWidth = Math.max(1.5, w * 0.002);
ctx.strokeRect(margin, margin, w - margin * 2, h - margin * 2);
// Title Block Area
const tbW = Math.max(120, Math.min(300, w * 0.25));
const tbH = Math.max(60, Math.min(150, h * 0.12));
const tbX = w - margin - tbW;
const tbY = h - margin - tbH;
ctx.strokeRect(tbX, tbY, tbW, tbH);
ctx.beginPath();
ctx.moveTo(tbX, tbY + tbH * 0.4);
ctx.lineTo(tbX + tbW, tbY + tbH * 0.4);
ctx.moveTo(tbX + tbW * 0.65, tbY + tbH * 0.4);
ctx.lineTo(tbX + tbW * 0.65, tbY + tbH);
ctx.stroke();
// Draft tick extensions (overshoot lines at corners)
const ext = margin * 0.6;
ctx.beginPath();
ctx.moveTo(margin - ext, margin); ctx.lineTo(margin, margin);
ctx.moveTo(margin, margin - ext); ctx.lineTo(margin, margin);
ctx.moveTo(w - margin + ext, margin); ctx.lineTo(w - margin, margin);
ctx.moveTo(w - margin, margin - ext); ctx.lineTo(w - margin, margin);
ctx.moveTo(margin - ext, h - margin); ctx.lineTo(margin, h - margin);
ctx.moveTo(margin, h - margin + ext); ctx.lineTo(margin, h - margin);
ctx.moveTo(w - margin + ext, h - margin); ctx.lineTo(w - margin, h - margin);
ctx.moveTo(w - margin, h - margin + ext); ctx.lineTo(w - margin, h - margin);
ctx.stroke();
// Title Block Skeleton Text
ctx.fillStyle = 'rgba(255, 255, 255, 0.75)';
ctx.font = `${Math.max(10, tbH * 0.2)}px monospace`;
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
ctx.fillText("PROJECT: SCHEMATIC", tbX + tbW * 0.05, tbY + tbH * 0.1);
ctx.fillText("SCALE: EST.", tbX + tbW * 0.05, tbY + tbH * 0.5);
ctx.fillText("REV: A", tbX + tbW * 0.7, tbY + tbH * 0.5);
return canvas;
}
Apply Changes