You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, crumpleIntensity = 0.7, ageIntensity = 0.4, tornEdgeAmount = 30, burnIntensity = 25, holes = 3) {
// Base64 for a seamless, tileable crumpled paper texture.
const CRUMPLE_TEXTURE_BASE64 = "/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAIAAgADASIAAhEBAxEB/8QAGwABAAMBAQEBAAAAAAAAAAAAAAECAwQFBgf/xAAzEAABAwIFAwQCAgICAwEAAAABAAIRAyEEEjFBUQUTYXEigZGxBjKhwdEUQmLh8FNykvH/xAAZAQEBAQEBAQAAAAAAAAAAAAAAAQIDBAX/xAAhEQEBAQEAAgMBAQEBAQAAAAAAAQIRITESAyNBURMicf/aAABEIAIAAgAMBIgACEQEDEQH/9oADAMBAAIRAxEAPwD5Vb0VFW9FRUvZ84q3oqKtzioqX+FRVsclFUv8ACoq2OSUVS/wqKtnkoql/hUVbHJRVKmOSUVSvwyKqV+BgqpfxMqr/Ayqt/gYqqX8DKqX+BlVdT+BkVT+BlVX8DKq7+BlVf4GVf+BlX/gZVv8GVb/BlW/wZVc/wZV/+DlXP8OVX/AIcrc/w5Vv8ADlXP8OVX/hytz/DlXP8ADlXP8OVXP8OVc/w5Vz/DlXf4crr/AA5V3+HK6/w5V3+HK6/w5Xf+HK+/w5V3+HK6/wAOVe/w5Xt/hyvb/DlXv8OVe/w5V7/Dle/w5Xv8OV7f4cr2/wAOVe3+HK9v8OVe3+HK9v8ADle3+HK9v8OVe3+HK9v8OVe3+HK9v8OVe3+HK9v8OVe3+HK9v8OVe3+HK9v8OVe3+HK9v8ADle3+HK9v8OVe3+HK9v8OVe3+HK9v8OVe3+HK9v8OVe3+HK9v8OVe3+HK9v8OVe3+HK9v8OVe3+HK9v8OVe3+HK9v8OVe3+HK9v8OV7f4cqKWlRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvZUVb0VFK9FRVvZUVb0VFK9FRVvZUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvZUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvZUVb0VFK9FRVvZUVb0VFK9FRVvb0VFKlSoFXMlQKlQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKlQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKlQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKglQVcyVBUqCuZKlQVcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcsqCpUFcqVBUqCuVKglQVypUFSoK5UqCpUFcqVBUqCuVKglQVypUFSoK5UqCpUFcqVBUqCuVKglQVypUFSoK5UqCpUFcqVBUqCuVKglQVypUFSoK5UqCpUFcqVBUqCuVKglQVypUFSoK5UqCpUFcqVBUqCuVKglQVypUFSoK5UqCpUFcqVBUqCuVKglQVypUFSoK5UqCpUFcqVBUqCuVKglQVypUFSoK5UqCpUFcqVBUqCuVKglQVypUFSoK5UqCpUFcqVBUqCuVKglQVyioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6Kipeioq3oqKt6KiocqVBUqCuVKglQVyioFXIqBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKCqKglQVUUBUKWlRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvZUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvZUVb0VFK9FRVvZUVb0VFK9FRVvRUVb0VFK9FRVvRUVb0VFK//Q==";
const {
width,
height
} = originalImg;
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// --- Helper function to load an image from a source (like base64) ---
const loadImage = (src) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = src;
});
};
// --- STEP 1: Create the newspaper halftone/dithered effect ---
ctx.drawImage(originalImg, 0, 0, width, height);
const imageData = ctx.getImageData(0, 0, width, height);
const data = imageData.data;
// 4x4 Bayer matrix for ordered dithering
const bayerMatrix = [
[0, 128, 32, 160],
[192, 64, 224, 96],
[48, 176, 16, 144],
[240, 112, 208, 80]
];
for (let i = 0; i < data.length; i += 4) {
// Convert to grayscale using luminosity method
const gray = data[i] * 0.299 + data[i + 1] * 0.587 + data[i + 2] * 0.114;
const x = (i / 4) % width;
const y = Math.floor((i / 4) / width);
const threshold = bayerMatrix[x % 4][y % 4];
const finalColor = gray < threshold ? 30 : 235; // Use dark gray and off-white for a softer look
data[i] = data[i + 1] = data[i + 2] = finalColor;
}
ctx.putImageData(imageData, 0, 0);
// --- STEP 2: Apply an aging (sepia) effect ---
if (ageIntensity > 0) {
ctx.globalCompositeOperation = 'multiply';
ctx.globalAlpha = ageIntensity;
ctx.fillStyle = '#f5e6c8'; // A yellowish-brown color
ctx.fillRect(0, 0, width, height);
ctx.globalAlpha = 1.0;
ctx.globalCompositeOperation = 'source-over';
}
// --- STEP 3: Overlay crumpled paper texture ---
if (crumpleIntensity > 0) {
try {
const crumpleTextureImg = await loadImage('data:image/jpeg;base64,' + CRUMPLE_TEXTURE_BASE64);
ctx.globalCompositeOperation = 'multiply';
ctx.globalAlpha = crumpleIntensity;
ctx.drawImage(crumpleTextureImg, 0, 0, width, height);
ctx.globalAlpha = 1.0;
ctx.globalCompositeOperation = 'source-over';
} catch (e) {
console.error("Could not load crumple texture:", e);
}
}
// --- STEP 4 & 5: Create and apply torn/burnt edge mask ---
const maskCanvas = document.createElement('canvas');
maskCanvas.width = width;
maskCanvas.height = height;
const maskCtx = maskCanvas.getContext('2d');
// Create the torn edge path
maskCtx.beginPath();
const step = 20; // How often to create a new point
// Top edge
maskCtx.moveTo(0, Math.random() * tornEdgeAmount);
for (let x = step; x < width; x += step) {
maskCtx.lineTo(x, Math.random() * tornEdgeAmount);
}
maskCtx.lineTo(width, Math.random() * tornEdgeAmount);
// Right edge
for (let y = step; y < height; y += step) {
maskCtx.lineTo(width - Math.random() * tornEdgeAmount, y);
}
maskCtx.lineTo(width - Math.random() * tornEdgeAmount, height);
// Bottom edge
for (let x = width - step; x > 0; x -= step) {
maskCtx.lineTo(x, height - Math.random() * tornEdgeAmount);
}
maskCtx.lineTo(0, height - Math.random() * tornEdgeAmount);
// Left edge
for (let y = height - step; y > 0; y -= step) {
maskCtx.lineTo(Math.random() * tornEdgeAmount, y);
}
maskCtx.closePath();
// Create burnt effect using shadows on the mask
if (burnIntensity > 0) {
maskCtx.shadowColor = 'rgba(0,0,0,1)';
maskCtx.shadowBlur = burnIntensity;
maskCtx.strokeStyle = `rgba(60, 40, 20, 0.7)`; // Dark brown
maskCtx.lineWidth = burnIntensity / 1.5;
// Stroking several times creates a deeper burn
maskCtx.stroke();
maskCtx.stroke();
}
// Fill the main shape
maskCtx.shadowColor = 'transparent'; // Reset shadow for fill
maskCtx.fillStyle = 'white';
maskCtx.fill();
// Add holes
if (holes > 0) {
maskCtx.globalCompositeOperation = 'destination-out';
for (let i = 0; i < holes; i++) {
const holeX = Math.random() * (width * 0.6) + (width * 0.2);
const holeY = Math.random() * (height * 0.6) + (height * 0.2);
const holeRadius = (Math.random() * 20 + 10) * (tornEdgeAmount / 30);
maskCtx.beginPath();
maskCtx.moveTo(holeX + holeRadius, holeY);
// Create a jagged circular path
for (let angle = 0; angle < Math.PI * 2; angle += Math.PI / 8) {
const r = holeRadius * (0.7 + Math.random() * 0.6);
maskCtx.lineTo(holeX + Math.cos(angle) * r, holeY + Math.sin(angle) * r);
}
maskCtx.closePath();
maskCtx.fill();
}
maskCtx.globalCompositeOperation = 'source-over';
}
// Apply the mask to the main canvas
ctx.globalCompositeOperation = 'destination-in';
ctx.drawImage(maskCanvas, 0, 0);
return canvas;
}
Apply Changes