You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, numRounds = 5, effectIntensity = 5, layout = 'grid') {
// --- Helper Functions for YTP-style Effects ---
/**
* Applies a random hue rotation, saturation, and contrast change.
* The `drawImage` call is necessary to bake the filter into the canvas pixels.
*/
const applyColorShift = (ctx, width, height, intensity) => {
const hue = (Math.random() - 0.5) * 45 * (intensity / 5);
const saturate = 100 + Math.random() * 20 * intensity;
const contrast = 100 + Math.random() * 10 * intensity;
ctx.save();
ctx.filter = `hue-rotate(${hue}deg) saturate(${saturate}%) contrast(${contrast}%)`;
ctx.drawImage(ctx.canvas, 0, 0);
ctx.restore();
};
/**
* Inverts the colors of the canvas.
*/
const applyInvert = (ctx, width, height, intensity) => {
if (Math.random() < 0.1 * intensity) { // Only apply this sometimes as it's very strong
ctx.save();
ctx.filter = 'invert(1)';
ctx.drawImage(ctx.canvas, 0, 0);
ctx.restore();
}
};
/**
* Pixelates the image. This is done manually by sampling and redrawing blocks.
*/
const applyPixelate = (ctx, width, height, intensity) => {
const size = Math.max(2, Math.floor(intensity * 1.5 * Math.random()));
for (let y = 0; y < height; y += size) {
for (let x = 0; x < width; x += size) {
const pixel = ctx.getImageData(x, y, 1, 1).data;
ctx.fillStyle = `rgba(${pixel[0]}, ${pixel[1]}, ${pixel[2]}, ${pixel[3] / 255})`;
ctx.fillRect(x, y, size, size);
}
}
};
/**
* Mirrors one half of the image over the other.
*/
const applyMirror = (ctx, width, height, intensity) => {
ctx.save();
if (Math.random() > 0.5) { // Horizontal mirror
const halfWidth = Math.ceil(width / 2);
ctx.drawImage(ctx.canvas, 0, 0, halfWidth, height, 0, 0, halfWidth, height); // Draw left half
ctx.scale(-1, 1);
ctx.drawImage(ctx.canvas, 0, 0, halfWidth, height, -width, 0, halfWidth, height); // Draw mirrored left half on the right
} else { // Vertical mirror
const halfHeight = Math.ceil(height / 2);
ctx.drawImage(ctx.canvas, 0, 0, width, halfHeight, 0, 0, width, halfHeight); // Draw top half
ctx.scale(1, -1);
ctx.drawImage(ctx.canvas, 0, 0, width, halfHeight, 0, -height, width, halfHeight); // Draw mirrored top half on the bottom
}
ctx.restore();
};
/**
* Stutters or repeats a small section of the image nearby.
*/
const applyStutter = (ctx, width, height, intensity) => {
const sx = Math.random() * width * 0.7;
const sy = Math.random() * height * 0.7;
const sw = width * (0.1 + Math.random() * 0.3);
const sh = height * (0.1 + Math.random() * 0.3);
const numRepeats = 1 + Math.floor(Math.random() * (intensity / 2));
for (let i = 0; i < numRepeats; i++) {
const dx = sx + (Math.random() - 0.5) * 40;
const dy = sy + (Math.random() - 0.5) * 40;
ctx.drawImage(ctx.canvas, sx, sy, sw, sh, dx, dy, sw * (0.8 + Math.random() * 0.4), sh * (0.8 + Math.random() * 0.4));
}
};
/**
* Draws some classic "meme" text on the canvas.
*/
const drawRandomText = (ctx, width, height, intensity) => {
const texts = ['SUS', 'PINGAS', 'LOL', 'NOICE', 'BOI', 'YTP'];
const text = texts[Math.floor(Math.random() * texts.length)];
const fontSize = Math.floor(height / (8 - intensity / 2));
ctx.font = `bold ${fontSize}px Impact, sans-serif`;
ctx.fillStyle = 'white';
ctx.strokeStyle = 'black';
ctx.lineWidth = Math.max(2, fontSize / 12);
const textMetrics = ctx.measureText(text);
const x = Math.random() * (width - textMetrics.width);
const y = Math.random() * (height - fontSize) + fontSize;
ctx.strokeText(text, x, y);
ctx.fillText(text, x, y);
};
/**
* Selects and applies a random set of effects to a canvas context.
*/
const applyYtpEffects = (ctx, width, height, intensity) => {
const effects = [
applyColorShift,
applyInvert,
applyPixelate,
applyMirror,
applyStutter,
drawRandomText
];
// Apply more effects for higher intensity
const numEffectsToApply = 1 + Math.floor(Math.random() * (intensity / 2.5));
for (let i = 0; i < numEffectsToApply; i++) {
const randomEffect = effects[Math.floor(Math.random() * effects.length)];
randomEffect(ctx, width, height, intensity);
}
};
// --- Main Function Logic ---
// 1. Sanitize parameters
const safeNumRounds = Math.max(1, Math.min(20, Number(numRounds) || 5));
const safeIntensity = Math.max(1, Math.min(10, Number(effectIntensity) || 5));
// 2. Scale image for performance
const MAX_DIMENSION = 400;
let baseWidth = originalImg.width;
let baseHeight = originalImg.height;
if (baseWidth > MAX_DIMENSION || baseHeight > MAX_DIMENSION) {
if (baseWidth > baseHeight) {
baseHeight = Math.round(baseHeight * (MAX_DIMENSION / baseWidth));
baseWidth = MAX_DIMENSION;
} else {
baseWidth = Math.round(baseWidth * (MAX_DIMENSION / baseHeight));
baseHeight = MAX_DIMENSION;
}
}
const firstCanvas = document.createElement('canvas');
firstCanvas.width = baseWidth;
firstCanvas.height = baseHeight;
firstCanvas.getContext('2d').drawImage(originalImg, 0, 0, baseWidth, baseHeight);
const canvases = [firstCanvas];
// 3. Generate subsequent rounds by applying cumulative effects
for (let i = 1; i < safeNumRounds; i++) {
const prevCanvas = canvases[i - 1];
const currentCanvas = document.createElement('canvas');
currentCanvas.width = baseWidth;
currentCanvas.height = baseHeight;
const ctx = currentCanvas.getContext('2d');
// Start with the previous round's image
ctx.drawImage(prevCanvas, 0, 0);
// Apply a new set of random effects
applyYtpEffects(ctx, baseWidth, baseHeight, safeIntensity);
canvases.push(currentCanvas);
}
// 4. Create the final display container and layout the canvases
const container = document.createElement('div');
container.style.display = 'flex';
container.style.gap = '15px';
container.style.flexWrap = 'wrap';
container.style.justifyContent = 'center';
container.style.fontFamily = 'Arial, sans-serif';
container.style.color = '#333';
if (layout === 'grid') {
container.style.display = 'grid';
let columns = Math.ceil(Math.sqrt(safeNumRounds));
if (safeNumRounds <= 3) columns = safeNumRounds;
container.style.gridTemplateColumns = `repeat(${columns}, 1fr)`;
container.style.maxWidth = `${columns * (baseWidth + 30)}px`;
} else if (layout === 'vertical') {
container.style.flexDirection = 'column';
container.style.alignItems = 'center';
} else { // 'horizontal' is the default fallback
container.style.flexDirection = 'row';
}
canvases.forEach((canvas, index) => {
const roundWrapper = document.createElement('div');
roundWrapper.style.padding = '10px';
roundWrapper.style.border = '1px solid #ccc';
roundWrapper.style.borderRadius = '8px';
roundWrapper.style.backgroundColor = '#f9f9f9';
roundWrapper.style.textAlign = 'center';
roundWrapper.style.boxShadow = '0 2px 4px rgba(0,0,0,0.1)';
const label = document.createElement('p');
label.textContent = `Round ${index + 1}`;
label.style.margin = '0 0 8px 0';
label.style.fontWeight = 'bold';
canvas.style.display = 'block';
canvas.style.borderRadius = '4px';
roundWrapper.appendChild(label);
roundWrapper.appendChild(canvas);
container.appendChild(roundWrapper);
});
return container;
}
Apply Changes