You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, foldsX = "4", foldsY = "3", effectIntensity = "0.8", includePierre = "true") {
// Create canvas
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const width = originalImg.width;
const height = originalImg.height;
canvas.width = width;
canvas.height = height;
// Draw original image
ctx.drawImage(originalImg, 0, 0);
// Parse parameters
const pFoldsX = Math.max(1, parseInt(foldsX, 10) || 4);
const pFoldsY = Math.max(1, parseInt(foldsY, 10) || 3);
const pIntensity = Math.min(1, Math.max(0, parseFloat(effectIntensity) || 0.8));
const pPierre = String(includePierre).toLowerCase() !== 'false' && String(includePierre) !== '0';
// 1. Draw Map Fold Shading
const cellW = width / pFoldsX;
const cellH = height / pFoldsY;
for (let x = 0; x < pFoldsX; x++) {
for (let y = 0; y < pFoldsY; y++) {
// Alternate shading to simulate physical folds on a map
const isDark = (x + y) % 2 === 0;
ctx.fillStyle = isDark ? `rgba(0, 0, 0, ${0.15 * pIntensity})` : `rgba(255, 255, 255, ${0.1 * pIntensity})`;
ctx.fillRect(x * cellW, y * cellH, cellW, cellH);
// Inner dark fold lines
ctx.strokeStyle = `rgba(0, 0, 0, ${0.3 * pIntensity})`;
ctx.lineWidth = 2;
ctx.strokeRect(x * cellW, y * cellH, cellW, cellH);
// Inner bright fold highlights for depth
ctx.strokeStyle = `rgba(255, 255, 255, ${0.15 * pIntensity})`;
ctx.lineWidth = 1;
ctx.strokeRect(x * cellW + 1, y * cellH + 1, cellW - 2, cellH - 2);
}
}
// 2. Apply warm vintage cartography filter (Sepia & paper noise)
const imgData = ctx.getImageData(0, 0, width, height);
const data = imgData.data;
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
// Sepia conversion math
const tr = (r * 0.393) + (g * 0.769) + (b * 0.189);
const tg = (r * 0.349) + (g * 0.686) + (b * 0.168);
const tb = (r * 0.272) + (g * 0.534) + (b * 0.131);
data[i] = r * (1 - pIntensity) + tr * pIntensity;
data[i + 1] = g * (1 - pIntensity) + tg * pIntensity;
data[i + 2] = b * (1 - pIntensity) + tb * pIntensity;
// Paper grain/noise
const noise = (Math.random() - 0.5) * 45 * pIntensity;
data[i] = Math.min(255, Math.max(0, data[i] + noise));
data[i + 1] = Math.min(255, Math.max(0, data[i + 1] + noise));
data[i + 2] = Math.min(255, Math.max(0, data[i + 2] + noise));
}
ctx.putImageData(imgData, 0, 0);
// 3. Add Vignette frame mimicking old map edges
const gradient = ctx.createRadialGradient(
width / 2, height / 2, Math.min(width, height) / 2.5,
width / 2, height / 2, Math.min(width, height)
);
gradient.addColorStop(0, 'rgba(0,0,0,0)');
gradient.addColorStop(1, `rgba(50, 30, 0, ${0.7 * pIntensity})`);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, height);
// 4. Optionally draw a stylized "Talking Pierre" the Parrot observing the map layout
function drawPierre(context, x, y, scale = 1) {
context.save();
context.translate(x, y);
context.scale(scale, scale);
// Shadow for separation from background
context.shadowColor = 'rgba(0,0,0,0.6)';
context.shadowBlur = 12;
context.shadowOffsetX = 3;
context.shadowOffsetY = 3;
// Parrot Body
context.fillStyle = '#4CAF50';
context.beginPath();
context.ellipse(0, 0, 50, 80, 0, 0, Math.PI * 2);
context.fill();
context.shadowColor = 'transparent'; // Outer shadow off for details
// Light Green Belly
context.fillStyle = '#81C784';
context.beginPath();
context.ellipse(15, 15, 35, 60, 0, 0, Math.PI * 2);
context.fill();
// Parrot Wing
context.fillStyle = '#388E3C';
context.beginPath();
context.ellipse(-25, 20, 20, 45, 15 * Math.PI / 180, 0, Math.PI * 2);
context.fill();
// Big Goofy Eye
context.fillStyle = 'white';
context.beginPath();
context.arc(20, -35, 23, 0, Math.PI * 2);
context.fill();
context.strokeStyle = '#2E7D32';
context.lineWidth = 2;
context.stroke();
// Eye Pupil
context.fillStyle = '#111';
context.beginPath();
context.arc(30, -35, 7, 0, Math.PI * 2);
context.fill();
// Upper Beak
context.fillStyle = '#FBC02D';
context.beginPath();
context.moveTo(40, -25);
context.bezierCurveTo(80, -25, 70, 10, 35, 0);
context.fill();
// Lower Beak
context.fillStyle = '#E64A19';
context.beginPath();
context.moveTo(37, -2);
context.bezierCurveTo(55, 5, 50, 15, 28, 10);
context.fill();
// Head Crest / Feathers
context.fillStyle = '#4CAF50';
context.beginPath();
context.moveTo(0, -75);
context.bezierCurveTo(15, -100, -10, -110, -5, -80);
context.moveTo(-15, -70);
context.bezierCurveTo(-25, -95, -45, -90, -20, -65);
context.fill();
context.restore();
}
if (pPierre) {
// Dynamically scale him based on image dimensions
const scale = Math.max(0.4, Math.min(width, height) / 600);
drawPierre(ctx, width - 90 * scale, height - 100 * scale, scale);
}
return canvas;
}
Apply Changes