You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, gridSize = 3, modification = "flip-x") {
// Ensure grid size is within reasonable bounds (min 2x2, max 10x10)
const size = Math.max(2, Math.min(10, parseInt(gridSize) || 3));
// Create the main wrapper container
const wrapper = document.createElement('div');
wrapper.style.fontFamily = 'system-ui, -apple-system, sans-serif';
wrapper.style.textAlign = 'center';
wrapper.style.padding = '20px';
wrapper.style.backgroundColor = '#f4f4f9';
wrapper.style.borderRadius = '12px';
wrapper.style.boxShadow = '0 8px 16px rgba(0,0,0,0.1)';
wrapper.style.boxSizing = 'border-box';
wrapper.style.width = '100%';
// Create the Header and Instructions
const header = document.createElement('h2');
header.innerText = 'Find the Odd One Out!';
header.style.margin = '0 0 10px 0';
header.style.color = '#2c3e50';
const subheading = document.createElement('p');
subheading.innerText = 'Can you spot the image that is slightly different? Click on it!';
subheading.style.margin = '0 0 20px 0';
subheading.style.color = '#7f8c8d';
subheading.style.fontSize = '1.1em';
wrapper.appendChild(header);
wrapper.appendChild(subheading);
// Create the Grid Container for the images
const container = document.createElement('div');
container.style.display = 'grid';
container.style.gridTemplateColumns = `repeat(${size}, 1fr)`;
container.style.gap = '10px';
container.style.maxWidth = '800px';
container.style.margin = '0 auto';
const totalCells = size * size;
const oddIndex = Math.floor(Math.random() * totalCells);
let cw = originalImg.width;
let ch = originalImg.height;
// Scale down the rendering dimension to prevent memory issues for large images in dense grids
const maxRenderSize = Math.max(100, 800 / size);
if (cw > maxRenderSize * 2 || ch > maxRenderSize * 2) {
const scale = Math.min((maxRenderSize * 2) / cw, (maxRenderSize * 2) / ch);
cw = Math.round(cw * scale);
ch = Math.round(ch * scale);
}
// Populate the Grid
for (let i = 0; i < totalCells; i++) {
const canvas = document.createElement('canvas');
canvas.width = cw;
canvas.height = ch;
canvas.style.width = '100%';
canvas.style.height = 'auto'; // Maintains aspect ratio visually
canvas.style.borderRadius = '8px';
canvas.style.cursor = 'pointer';
canvas.style.border = '4px solid transparent';
canvas.style.transition = 'transform 0.2s, border-color 0.2s, opacity 0.3s';
canvas.style.boxShadow = '0 4px 6px rgba(0,0,0,0.05)';
canvas.style.backgroundColor = '#fff';
const ctx = canvas.getContext('2d');
// Render either the Odd One Out or a normal image
if (i === oddIndex) {
ctx.save();
switch (modification) {
case 'flip-x':
ctx.translate(cw, 0);
ctx.scale(-1, 1);
break;
case 'flip-y':
ctx.translate(0, ch);
ctx.scale(1, -1);
break;
case 'rotate-180':
ctx.translate(cw / 2, ch / 2);
ctx.rotate(Math.PI);
ctx.translate(-cw / 2, -ch / 2);
break;
case 'hue-rotate':
ctx.filter = 'hue-rotate(45deg)';
break;
case 'grayscale':
ctx.filter = 'grayscale(100%)';
break;
case 'invert':
ctx.filter = 'invert(100%)';
break;
case 'brightness':
ctx.filter = 'brightness(75%)';
break;
case 'blur':
ctx.filter = `blur(${Math.max(1, cw / 100)}px)`;
break;
case 'sepia':
ctx.filter = 'sepia(100%)';
break;
default:
// Fallback to flip-x
ctx.translate(cw, 0);
ctx.scale(-1, 1);
break;
}
ctx.drawImage(originalImg, 0, 0, cw, ch);
ctx.restore();
// Interaction logic for correct answer
canvas.onclick = () => {
const allCanvases = container.querySelectorAll('canvas');
allCanvases.forEach(c => {
c.style.pointerEvents = 'none'; // Disable further clicks
if (c !== canvas) {
c.style.opacity = '0.4';
c.style.border = '4px solid transparent';
}
});
canvas.style.border = '4px solid #2ecc71';
canvas.style.transform = 'scale(1.05)';
canvas.style.zIndex = '10';
canvas.style.position = 'relative';
subheading.innerHTML = '🎉 <strong>Correct! You found the odd one out!</strong> 🎉';
subheading.style.color = '#27ae60';
// Playful jump animation (Web Animations API)
if (typeof canvas.animate === 'function') {
canvas.animate([
{ transform: 'scale(1.05) translateY(0px)' },
{ transform: 'scale(1.05) translateY(-10px)' },
{ transform: 'scale(1.05) translateY(0px)' }
], { duration: 300, iterations: 2 });
}
};
} else {
ctx.drawImage(originalImg, 0, 0, cw, ch);
// Interaction logic for wrong answers
canvas.onclick = () => {
canvas.style.border = '4px solid #e74c3c';
// Shake animation signifying error
if (typeof canvas.animate === 'function') {
canvas.animate([
{ transform: 'translateX(0px)' },
{ transform: 'translateX(-6px)' },
{ transform: 'translateX(6px)' },
{ transform: 'translateX(-6px)' },
{ transform: 'translateX(6px)' },
{ transform: 'translateX(0px)' }
], { duration: 450 });
}
// Automatically clear the red border shortly after
setTimeout(() => {
canvas.style.border = '4px solid transparent';
}, 800);
};
}
container.appendChild(canvas);
}
wrapper.appendChild(container);
return wrapper;
}
Apply Changes