You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, cookieSize = 25, backgroundFill = "transparent") {
// Parse settings and ensure valid bounds
const size = Math.max(5, parseInt(cookieSize, 10) || 25);
const w = originalImg.width;
const h = originalImg.height;
// Create off-screen canvas to extract pixel colors from the original image
const offCanvas = document.createElement('canvas');
offCanvas.width = w;
offCanvas.height = h;
const offCtx = offCanvas.getContext('2d', { willReadFrequently: true });
offCtx.drawImage(originalImg, 0, 0);
const imgData = offCtx.getImageData(0, 0, w, h);
const data = imgData.data;
// Create the final canvas for the cookie mosaic
const canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
// Handle optional background filler
if (backgroundFill.toLowerCase() !== "transparent") {
ctx.fillStyle = backgroundFill;
ctx.fillRect(0, 0, w, h);
}
// Mathematical pseudo-random number generator for consistent baking irregularities (chips, shape)
function random(seed) {
let x = Math.sin(seed) * 10000;
return x - Math.floor(x);
}
// Process the image in blocks formatted as cookie cells
for (let y = 0; y < h; y += size) {
for (let x = 0; x < w; x += size) {
let rAcc = 0, gAcc = 0, bAcc = 0, aAcc = 0, count = 0;
const maxDx = Math.min(size, w - x);
const maxDy = Math.min(size, h - y);
// Harvest the average pixel color of the current block
for (let dy = 0; dy < maxDy; dy++) {
for (let dx = 0; dx < maxDx; dx++) {
const idx = ((y + dy) * w + (x + dx)) * 4;
rAcc += data[idx];
gAcc += data[idx + 1];
bAcc += data[idx + 2];
aAcc += data[idx + 3];
count++;
}
}
if (count === 0) continue;
// Skip fully/mostly transparent pixels
const alpha = Math.round(aAcc / count);
if (alpha < 10) continue;
// Calculate final base block colors
const r = Math.round(rAcc / count);
const g = Math.round(gAcc / count);
const b = Math.round(bAcc / count);
const cx = x + size / 2;
const cy = y + size / 2;
// Adjust radius slightly if alpha is translucent
const alphaScale = Math.min(1, alpha / 255);
const radius = size * 0.45 * alphaScale;
if (radius < 1) continue; // Safety check
// Unique seed for this individual cookie
let seed = x * 41 + y * 23;
// 1. Draw Wobbly Cookie Base Shape (gives organic, baked feel)
ctx.beginPath();
const pointCount = 10;
for (let i = 0; i <= pointCount; i++) {
const angle = (i / pointCount) * Math.PI * 2;
// Variance allows radius to fluctuate between 85% and 100%
const rVar = radius * (0.85 + 0.15 * random(seed++));
const px = cx + Math.cos(angle) * rVar;
const py = cy + Math.sin(angle) * rVar;
if (i === 0) ctx.moveTo(px, py);
else ctx.lineTo(px, py);
}
ctx.closePath();
// Extrude color and shadow the edges slightly for depth
ctx.fillStyle = `rgb(${r},${g},${b})`;
ctx.fill();
ctx.lineWidth = radius * 0.15;
ctx.strokeStyle = `rgba(0,0,0,0.25)`;
ctx.stroke();
// 2. Add subtle 3D highlight across the top crust
ctx.beginPath();
ctx.arc(cx, cy, radius * 0.7, Math.PI * 1.1, Math.PI * 1.9);
ctx.strokeStyle = `rgba(255, 255, 255, 0.2)`;
ctx.lineWidth = radius * 0.15;
ctx.stroke();
// 3. Bake in the darker chocolate-chips using a proportionally darker shade!
const chipR = Math.round(r * 0.4);
const chipG = Math.round(g * 0.4);
const chipB = Math.round(b * 0.4);
const chipColor = `rgb(${chipR},${chipG},${chipB})`;
// Generate between 3 and 7 chips per cookie
const numChips = Math.floor(3 + 5 * random(seed++));
for (let i = 0; i < numChips; i++) {
const angle = random(seed++) * Math.PI * 2;
const dist = random(seed++) * radius * 0.55;
const chipRadius = radius * (0.15 + 0.1 * random(seed++));
const chipX = cx + Math.cos(angle) * dist;
const chipY = cy + Math.sin(angle) * dist;
ctx.beginPath();
ctx.arc(chipX, chipY, chipRadius, 0, Math.PI * 2);
ctx.fillStyle = chipColor;
ctx.fill();
}
}
}
return canvas;
}
Apply Changes