You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, elementLimit = 999) {
// 1. Define the palette of emojis and their representative RGB colors.
// This palette is chosen to provide a decent range of colors and shades for a good result.
const palette = [
{ emoji: "⚫️", r: 0, g: 0, b: 0 }, // Black
{ emoji: "⬛️", r: 44, g: 48, b: 53 }, // Dark Gray
{ emoji: "🟫", r: 153, g: 107, b: 72 }, // Brown
{ emoji: "🟥", r: 221, g: 46, b: 68 }, // Red
{ emoji: "🟧", r: 245, g: 146, b: 51 }, // Orange
{ emoji: "🟨", r: 255, g: 222, b: 51 }, // Yellow
{ emoji: "🟩", r: 122, g: 199, b: 87 }, // Green
{ emoji: "🟦", r: 85, g: 172, b: 238 }, // Blue
{ emoji: "🟪", r: 170, g: 142, b: 214 }, // Purple
{ emoji: "⬜️", r: 230, g: 231, b: 232 }, // Light Gray
{ emoji: "⚪️", r: 255, g: 255, b: 255 }, // White
// Skin tones are added for more nuanced shades, especially for portraits
{ emoji: "🏿", r: 66, g: 43, b: 31 },
{ emoji: "🏾", r: 125, g: 84, b: 57 },
{ emoji: "🏽", r: 188, g: 132, b: 88 },
{ emoji: "🏼", r: 242, g: 196, b: 151 },
{ emoji: "🏻", r: 255, g: 228, b: 191 },
];
// Helper function to find the closest emoji for a given color.
// It calculates the squared Euclidean distance in the RGB color space for efficiency.
function findClosestEmoji(r, g, b) {
let closestEmoji = '';
let minDistanceSq = Infinity;
for (const item of palette) {
const dr = r - item.r;
const dg = g - item.g;
const db = b - item.b;
const distanceSq = dr * dr + dg * dg + db * db;
if (distanceSq < minDistanceSq) {
minDistanceSq = distanceSq;
closestEmoji = item.emoji;
}
}
return closestEmoji;
}
// 2. Sanitize and clamp the elementLimit parameter to a reasonable range.
let maxElements = parseInt(elementLimit, 10) || 999;
maxElements = Math.max(16, Math.min(maxElements, 4000));
// 3. Calculate the dimensions of the emoji grid while preserving the image's aspect ratio.
const aspectRatio = originalImg.width / originalImg.height;
const heightInBlocks = Math.round(Math.sqrt(maxElements / aspectRatio));
const widthInBlocks = Math.round(heightInBlocks * aspectRatio);
// 4. Use a temporary canvas to downsample the image.
// This is an efficient way to get the average color for each emoji block.
const tempCanvas = document.createElement('canvas');
tempCanvas.width = widthInBlocks;
tempCanvas.height = heightInBlocks;
const tempCtx = tempCanvas.getContext('2d');
// Disabling image smoothing helps create sharper, more distinct blocks.
tempCtx.imageSmoothingEnabled = false;
tempCtx.drawImage(originalImg, 0, 0, widthInBlocks, heightInBlocks);
const imageData = tempCtx.getImageData(0, 0, widthInBlocks, heightInBlocks).data;
// 5. Build the string of emojis by iterating through the downsampled pixel data.
let emojiArt = '';
for (let y = 0; y < heightInBlocks; y++) {
for (let x = 0; x < widthInBlocks; x++) {
const index = (y * widthInBlocks + x) * 4;
const r = imageData[index];
const g = imageData[index + 1];
const b = imageData[index + 2];
const a = imageData[index + 3];
// If the pixel is mostly transparent, use a light emoji. Otherwise, find the best match.
if (a < 128) {
emojiArt += '⬜️';
} else {
emojiArt += findClosestEmoji(r, g, b);
}
}
emojiArt += '\n'; // Add a newline after each row.
}
// 6. Create the final display element.
const outputElement = document.createElement('div');
outputElement.textContent = emojiArt;
// Style the element for proper display as fixed-grid art.
outputElement.style.fontFamily = 'monospace';
outputElement.style.lineHeight = '1';
outputElement.style.textAlign = 'center';
outputElement.style.display = 'inline-block';
outputElement.style.whiteSpace = 'pre'; // Ensures newlines and spacing are respected.
// Calculate a font size to make the output's width reasonable.
const desiredWidth = Math.min(600, originalImg.width);
const fontSize = desiredWidth / widthInBlocks;
// Clamp font size to a readable range.
outputElement.style.fontSize = `${Math.max(4, Math.min(fontSize, 24))}px`;
return outputElement;
}
Apply Changes