You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Converts an image into a grid of emojis by mapping pixel colors to a fixed emoji palette.
* The function analyzes the image in blocks, calculates the average color for each block,
* and finds the closest matching emoji from the provided set.
*
* @param {Image} originalImg The source JavaScript Image object to convert. The image must be fully loaded.
* @param {number} [emojiSize=10] The size of the square pixel area to be averaged into a single emoji. A smaller number yields more detail.
* @returns {HTMLElement} A <pre> element containing the grid of emojis, which can be directly displayed.
*/
function processImage(originalImg, emojiSize = 10) {
// Ensure emojiSize is a valid positive integer.
const size = Math.max(1, Math.floor(emojiSize));
// Define the color palette based on the specified emojis using standard web color RGB values.
const palette = [
{ emoji: '🟥', r: 255, g: 0, b: 0 }, // Red
{ emoji: '🟧', r: 255, g: 165, b: 0 }, // Orange
{ emoji: '🟨', r: 255, g: 255, b: 0 }, // Yellow
{ emoji: '🟩', r: 0, g: 128, b: 0 }, // Green
{ emoji: '🟦', r: 0, g: 0, b: 255 }, // Blue
{ emoji: '🟪', r: 128, g: 0, b: 128 }, // Purple
{ emoji: '🟫', r: 165, g: 42, b: 42 }, // Brown
{ emoji: '⬛', r: 0, g: 0, b: 0 }, // Black
{ emoji: '⬜', r: 255, g: 255, b: 255 } // White
];
/**
* Finds the closest emoji from the palette for a given RGB color using the
* Euclidean distance formula in the RGB color space.
* @param {number} r The red channel value (0-255) of the pixel.
* @param {number} g The green channel value (0-255) of the pixel.
* @param {number} b The blue channel value (0-255) of the pixel.
* @returns {string} The emoji character that best represents the input color.
*/
function findClosestEmoji(r, g, b) {
let minDistanceSq = Infinity;
let closestEmoji = ' ';
for (const color of palette) {
const dR = r - color.r;
const dG = g - color.g;
const dB = b - color.b;
const distanceSq = dR * dR + dG * dG + dB * dB;
if (distanceSq < minDistanceSq) {
minDistanceSq = distanceSq;
closestEmoji = color.emoji;
}
}
return closestEmoji;
}
// Create a canvas to access the image's pixel data.
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
canvas.width = originalImg.width;
canvas.height = originalImg.height;
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
let emojiGridString = '';
try {
// Get all pixel data from the canvas at once for performance.
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
const width = canvas.width;
// Iterate over the image in blocks of `size` x `size` pixels.
for (let y = 0; y < canvas.height; y += size) {
for (let x = 0; x < canvas.width; x += size) {
let sumR = 0, sumG = 0, sumB = 0, pixelCount = 0;
// Loop through each pixel within the current block to find its average color.
for (let blockY = y; blockY < y + size && blockY < canvas.height; blockY++) {
for (let blockX = x; blockX < x + size && blockX < canvas.width; blockX++) {
const index = (blockY * width + blockX) * 4;
sumR += data[index];
sumG += data[index + 1];
sumB += data[index + 2];
// Alpha channel (data[index + 3]) is ignored for simplicity.
pixelCount++;
}
}
if (pixelCount === 0) continue;
const avgR = sumR / pixelCount;
const avgG = sumG / pixelCount;
const avgB = sumB / pixelCount;
// Find the closest emoji for the block's average color and append it.
emojiGridString += findClosestEmoji(avgR, avgG, avgB);
}
emojiGridString += '\n'; // Add a newline to start a new row of emojis.
}
} catch (e) {
console.error("Could not process image pixels.", e);
const errorElement = document.createElement('div');
errorElement.textContent = "Error: Could not access image pixels. This may be due to a Cross-Origin (CORS) issue if you're using an image from another website.";
errorElement.style.color = "red";
errorElement.style.backgroundColor = "#fff0f0";
errorElement.style.padding = "10px";
errorElement.style.border = "1px solid red";
errorElement.style.fontFamily = "sans-serif";
return errorElement;
}
// Create a <pre> element to display the emoji grid, preserving the line breaks.
const pre = document.createElement('pre');
pre.textContent = emojiGridString.trim();
// Apply styles to make the emoji grid look like a coherent image.
pre.style.fontFamily = 'monospace';
pre.style.fontSize = '10px';
pre.style.lineHeight = '9px'; // A tight line height helps form a solid block.
pre.style.letterSpacing = '0';
pre.style.margin = '0';
pre.style.display = 'inline-block'; // Ensures the element fits its content.
return pre;
}
Apply Changes