You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, blockSize = 10, outputType = 'flags', fontSize = 0) {
// 1. Sanitize parameters
blockSize = Math.max(1, parseInt(blockSize, 10)) || 10;
fontSize = Math.max(0, parseInt(fontSize, 10)) || 0;
const finalFontSize = (fontSize <= 0) ? Math.floor(blockSize * 0.8) : fontSize;
// 2. Define the flag data (colors, average RGB representations, and drawing functions)
const colors = {
white: '#FFFFFF',
red: '#C8102E',
blue: '#00205B',
yellow: '#FFD100',
black: '#000000',
};
const rgbColors = {
white: [255, 255, 255],
red: [200, 16, 46],
blue: [0, 32, 91],
yellow: [255, 209, 0],
black: [0, 0, 0],
};
const flagData = [{
letter: 'A', // Alpha
avgColor: [127, 143, 173], // 50% White, 50% Blue
draw: (ctx, x, y, s) => {
ctx.fillStyle = colors.white;
ctx.fillRect(x, y, s / 2, s);
ctx.fillStyle = colors.blue;
ctx.fillRect(x + s / 2, y, s / 2, s);
}
}, {
letter: 'B', // Bravo
avgColor: rgbColors.red,
draw: (ctx, x, y, s) => {
ctx.fillStyle = colors.red;
ctx.fillRect(x, y, s, s);
}
}, {
letter: 'C', // Charlie
avgColor: [142, 117, 147], // 40% Blue, 40% White, 20% Red
draw: (ctx, x, y, s) => {
const h = s / 5;
ctx.fillStyle = colors.blue; ctx.fillRect(x, y, s, h);
ctx.fillStyle = colors.white; ctx.fillRect(x, y + h, s, h);
ctx.fillStyle = colors.red; ctx.fillRect(x, y + 2 * h, s, h);
ctx.fillStyle = colors.white; ctx.fillRect(x, y + 3 * h, s, h);
ctx.fillStyle = colors.blue; ctx.fillRect(x, y + 4 * h, s, h);
}
}, {
letter: 'D', // Delta
avgColor: [170, 149, 30], // 67% Yellow, 33% Blue
draw: (ctx, x, y, s) => {
const h = s / 3;
ctx.fillStyle = colors.yellow; ctx.fillRect(x, y, s, h);
ctx.fillStyle = colors.blue; ctx.fillRect(x, y + h, s, 2 * h);
ctx.fillStyle = colors.yellow; ctx.fillRect(x, y + 2 * h, s, h);
}
}, {
letter: 'E', // Echo
avgColor: [100, 24, 68], // 50% Blue, 50% Red
draw: (ctx, x, y, s) => {
ctx.fillStyle = colors.blue; ctx.fillRect(x, y, s, s / 2);
ctx.fillStyle = colors.red; ctx.fillRect(x, y + s / 2, s, s / 2);
}
}, {
letter: 'F', // Foxtrot
avgColor: [227, 135, 150], // 50% White, 50% Red
draw: (ctx, x, y, s) => {
ctx.fillStyle = colors.white; ctx.fillRect(x, y, s, s);
ctx.fillStyle = colors.red;
ctx.beginPath();
ctx.moveTo(x + s / 2, y); ctx.lineTo(x + s, y + s / 2);
ctx.lineTo(x + s / 2, y + s); ctx.lineTo(x, y + s / 2);
ctx.closePath(); ctx.fill();
}
}, {
letter: 'G', // Golf
avgColor: [127, 120, 45], // 50% Yellow, 50% Blue
draw: (ctx, x, y, s) => {
const w = s / 6;
for (let i = 0; i < 6; i++) {
ctx.fillStyle = (i % 2 === 0) ? colors.yellow : colors.blue;
ctx.fillRect(x + i * w, y, w, s);
}
}
}, {
letter: 'H', // Hotel
avgColor: [227, 135, 150], // 50% White, 50% Red
draw: (ctx, x, y, s) => {
ctx.fillStyle = colors.white; ctx.fillRect(x, y, s / 2, s);
ctx.fillStyle = colors.red; ctx.fillRect(x + s / 2, y, s / 2, s);
}
}, {
letter: 'I', // India
avgColor: [204, 167, 0], // 80% Yellow, 20% Black
draw: (ctx, x, y, s) => {
ctx.fillStyle = colors.yellow; ctx.fillRect(x, y, s, s);
ctx.fillStyle = colors.black;
ctx.beginPath(); ctx.arc(x + s / 2, y + s / 2, s * 0.25, 0, 2 * Math.PI); ctx.fill();
}
}, {
letter: 'J', // Juliett
avgColor: [85, 105, 145], // 67% Blue, 33% White
draw: (ctx, x, y, s) => {
const h = s / 3;
ctx.fillStyle = colors.blue; ctx.fillRect(x, y, s, h);
ctx.fillStyle = colors.white; ctx.fillRect(x, y + h, s, h);
ctx.fillStyle = colors.blue; ctx.fillRect(x, y + 2 * h, s, h);
}
}, {
letter: 'K', // Kilo
avgColor: [127, 120, 45], // 50% Yellow, 50% Blue
draw: (ctx, x, y, s) => {
ctx.fillStyle = colors.yellow; ctx.fillRect(x, y, s / 2, s);
ctx.fillStyle = colors.blue; ctx.fillRect(x + s / 2, y, s / 2, s);
}
}, {
letter: 'L', // Lima
avgColor: [127, 120, 45], // 50% Yellow, 50% Blue
draw: (ctx, x, y, s) => {
const d = s / 2;
ctx.fillStyle = colors.yellow; ctx.fillRect(x, y, d, d);
ctx.fillStyle = colors.blue; ctx.fillRect(x + d, y, d, d);
ctx.fillStyle = colors.blue; ctx.fillRect(x, y + d, d, d);
ctx.fillStyle = colors.yellow; ctx.fillRect(x + d, y + d, d, d);
}
}, {
letter: 'M', // Mike
avgColor: [76, 98, 140], // 70% Blue, 30% White
draw: (ctx, x, y, s) => {
ctx.fillStyle = colors.blue; ctx.fillRect(x, y, s, s);
ctx.strokeStyle = colors.white; ctx.lineWidth = s * 0.2;
ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x + s, y + s); ctx.stroke();
ctx.beginPath(); ctx.moveTo(x + s, y); ctx.lineTo(x, y + s); ctx.stroke();
}
}, {
letter: 'N', // November
avgColor: [127, 143, 173], // 50% Blue, 50% White
draw: (ctx, x, y, s) => {
const d = s / 4;
for (let row = 0; row < 4; row++) {
for (let col = 0; col < 4; col++) {
ctx.fillStyle = ((row + col) % 2 === 0) ? colors.blue : colors.white;
ctx.fillRect(x + col * d, y + row * d, d, d);
}
}
}
}, {
letter: 'O', // Oscar
avgColor: [227, 112, 23], // 50% Yellow, 50% Red
draw: (ctx, x, y, s) => {
ctx.fillStyle = colors.red; ctx.fillRect(x, y, s, s);
ctx.fillStyle = colors.yellow;
ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x + s, y); ctx.lineTo(x, y + s);
ctx.closePath(); ctx.fill();
}
}, {
letter: 'P', // Papa
avgColor: [63, 83, 129], // 75% Blue, 25% White
draw: (ctx, x, y, s) => {
ctx.fillStyle = colors.blue; ctx.fillRect(x, y, s, s);
ctx.fillStyle = colors.white; ctx.fillRect(x + s * 0.2, y + s * 0.2, s * 0.6, s * 0.6);
}
}, {
letter: 'Q', // Quebec
avgColor: rgbColors.yellow,
draw: (ctx, x, y, s) => {
ctx.fillStyle = colors.yellow; ctx.fillRect(x, y, s, s);
}
}, {
letter: 'R', // Romeo
avgColor: [217, 78, 32], // 70% Red, 30% Yellow
draw: (ctx, x, y, s) => {
ctx.fillStyle = colors.red; ctx.fillRect(x, y, s, s);
const w = s * 0.2;
ctx.fillStyle = colors.yellow;
ctx.fillRect(x + s / 2 - w / 2, y, w, s);
ctx.fillRect(x, y + s / 2 - w / 2, s, w);
}
}, {
letter: 'S', // Sierra
avgColor: [191, 207, 219], // 75% White, 25% Blue
draw: (ctx, x, y, s) => {
ctx.fillStyle = colors.white; ctx.fillRect(x, y, s, s);
ctx.fillStyle = colors.blue; ctx.fillRect(x + s * 0.2, y + s * 0.2, s * 0.6, s * 0.6);
}
}, {
letter: 'T', // Tango
avgColor: [151, 101, 130], // 33% Red, 33% White, 33% Blue
draw: (ctx, x, y, s) => {
const w = s / 3;
ctx.fillStyle = colors.red; ctx.fillRect(x, y, w, s);
ctx.fillStyle = colors.white; ctx.fillRect(x + w, y, w, s);
ctx.fillStyle = colors.blue; ctx.fillRect(x + 2 * w, y, w, s);
}
}, {
letter: 'U', // Uniform
avgColor: [227, 135, 150], // 50% Red, 50% White
draw: (ctx, x, y, s) => {
const d = s / 2;
ctx.fillStyle = colors.red; ctx.fillRect(x, y, d, d);
ctx.fillStyle = colors.white; ctx.fillRect(x + d, y, d, d);
ctx.fillStyle = colors.white; ctx.fillRect(x, y + d, d, d);
ctx.fillStyle = colors.red; ctx.fillRect(x + d, y + d, d, d);
}
}, {
letter: 'V', // Victor
avgColor: [238, 184, 192], // 70% White, 30% Red
draw: (ctx, x, y, s) => {
ctx.fillStyle = colors.white; ctx.fillRect(x, y, s, s);
ctx.strokeStyle = colors.red; ctx.lineWidth = s * 0.2;
ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x + s, y + s); ctx.stroke();
ctx.beginPath(); ctx.moveTo(x + s, y); ctx.lineTo(x, y + s); ctx.stroke();
}
}, {
letter: 'W', // Whiskey
avgColor: [157, 146, 160], // 50% White, 35% Blue, 15% Red
draw: (ctx, x, y, s) => {
ctx.fillStyle = colors.blue; ctx.fillRect(x, y, s, s);
ctx.fillStyle = colors.white; ctx.fillRect(x + s * 0.15, y + s * 0.15, s * 0.7, s * 0.7);
ctx.fillStyle = colors.red; ctx.fillRect(x + s * 0.3, y + s * 0.3, s * 0.4, s * 0.4);
}
}, {
letter: 'X', // X-ray
avgColor: [178, 188, 214], // 70% White, 30% Blue
draw: (ctx, x, y, s) => {
ctx.fillStyle = colors.white; ctx.fillRect(x, y, s, s);
const w = s * 0.2;
ctx.fillStyle = colors.blue;
ctx.fillRect(x + s / 2 - w / 2, y, w, s);
ctx.fillRect(x, y + s / 2 - w / 2, s, w);
}
}, {
letter: 'Y', // Yankee
avgColor: [227, 112, 23], // 50% Red, 50% Yellow
draw: (ctx, x, y, s) => {
ctx.fillStyle = colors.yellow;
ctx.fillRect(x, y, s, s);
ctx.fillStyle = colors.red;
const stripeWidth = s / 5;
for (let i = -1; i < 5; i++) {
ctx.beginPath();
ctx.moveTo(x + i * stripeWidth, y);
ctx.lineTo(x + (i + 1) * stripeWidth, y);
ctx.lineTo(x + (i + 3) * stripeWidth, y + s);
ctx.lineTo(x + (i + 2) * stripeWidth, y + s);
ctx.closePath();
ctx.fill();
}
}
}, {
letter: 'Z', // Zulu
avgColor: [113, 56, 34], // 25% each of Y, R, K, B
draw: (ctx, x, y, s) => {
ctx.fillStyle = colors.yellow;
ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x + s, y); ctx.lineTo(x + s / 2, y + s / 2); ctx.closePath(); ctx.fill();
ctx.fillStyle = colors.red;
ctx.beginPath(); ctx.moveTo(x + s, y); ctx.lineTo(x + s, y + s); ctx.lineTo(x + s / 2, y + s / 2); ctx.closePath(); ctx.fill();
ctx.fillStyle = colors.black;
ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x, y + s); ctx.lineTo(x + s / 2, y + s / 2); ctx.closePath(); ctx.fill();
ctx.fillStyle = colors.blue;
ctx.beginPath(); ctx.moveTo(x, y + s); ctx.lineTo(x + s, y + s); ctx.lineTo(x + s / 2, y + s / 2); ctx.closePath(); ctx.fill();
}
}, ];
// 3. Helper Caching and Distance Function
const colorDistance = (c1, c2) => {
const dr = c1[0] - c2[0];
const dg = c1[1] - c2[1];
const db = c1[2] - c2[2];
return dr * dr + dg * dg + db * db; // Use squared distance for efficiency
};
// 4. Create Canvases
const sourceCanvas = document.createElement('canvas');
sourceCanvas.width = originalImg.width;
sourceCanvas.height = originalImg.height;
const sourceCtx = sourceCanvas.getContext('2d', { willReadFrequently: true });
sourceCtx.drawImage(originalImg, 0, 0);
const resultCanvas = document.createElement('canvas');
resultCanvas.width = originalImg.width;
resultCanvas.height = originalImg.height;
const resultCtx = resultCanvas.getContext('2d');
resultCtx.clearRect(0, 0, resultCanvas.width, resultCanvas.height);
// 5. Process Image in Blocks
for (let y = 0; y < originalImg.height; y += blockSize) {
for (let x = 0; x < originalImg.width; x += blockSize) {
// Get average color of the source block
const w = Math.min(blockSize, originalImg.width - x);
const h = Math.min(blockSize, originalImg.height - y);
const imageData = sourceCtx.getImageData(x, y, w, h);
const data = imageData.data;
let r = 0, g = 0, b = 0;
for (let i = 0; i < data.length; i += 4) {
r += data[i];
g += data[i + 1];
b += data[i + 2];
}
const pixelCount = data.length / 4;
const avgColor = [r / pixelCount, g / pixelCount, b / pixelCount];
// Find the closest matching flag from our data
let minDistance = Infinity;
let bestFlag = null;
for (const flag of flagData) {
const distance = colorDistance(avgColor, flag.avgColor);
if (distance < minDistance) {
minDistance = distance;
bestFlag = flag;
}
}
// Draw the result
if (bestFlag) {
if (outputType === 'letters') {
// Use the block's average color as the background
resultCtx.fillStyle = `rgb(${avgColor[0]}, ${avgColor[1]}, ${avgColor[2]})`;
resultCtx.fillRect(x, y, blockSize, blockSize);
// Choose a contrasting text color (black or white)
const brightness = (avgColor[0] * 299 + avgColor[1] * 587 + avgColor[2] * 114) / 1000;
resultCtx.fillStyle = (brightness > 125) ? 'black' : 'white';
resultCtx.font = `${finalFontSize}px monospace`;
resultCtx.textAlign = 'center';
resultCtx.textBaseline = 'middle';
resultCtx.fillText(bestFlag.letter, x + blockSize / 2, y + blockSize / 2);
} else { // Default to 'flags'
bestFlag.draw(resultCtx, x, y, blockSize);
}
}
}
}
// 6. Return the final canvas
return resultCanvas;
}
Apply Changes