You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, intensity = 0.5, rustColor = '#8b4513', dustColor = '#c2b280', scratchCount = 75, noiseAmount = 0.15) {
/**
* Helper function to convert a hex color string to an RGB object.
* @param {string} hex - The hex color string (e.g., '#RRGGBB').
* @returns {{r: number, g: number, b: number}|null} An object with r, g, b properties, or null if invalid.
*/
const hexToRgb = (hex) => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
};
const rustRgb = hexToRgb(rustColor);
const dustRgb = hexToRgb(dustColor);
if (!rustRgb || !dustRgb) {
console.error("Invalid hex color provided.");
return originalImg;
}
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = originalImg.naturalWidth;
canvas.height = originalImg.naturalHeight;
// --- 1. Generate a noise map to control rust and dust placement ---
const noiseCanvas = document.createElement('canvas');
const noiseCtx = noiseCanvas.getContext('2d');
const noiseSize = Math.max(32, Math.floor(canvas.width / 20)); // Low resolution noise
noiseCanvas.width = noiseSize;
noiseCanvas.height = Math.floor(noiseSize * (canvas.height / canvas.width));
const noiseImageData = noiseCtx.createImageData(noiseCanvas.width, noiseCanvas.height);
const noiseData = noiseImageData.data;
for (let i = 0; i < noiseData.length; i += 4) {
const value = Math.random() * 255;
noiseData[i] = value;
noiseData[i + 1] = value;
noiseData[i + 2] = value;
noiseData[i + 3] = 255;
}
noiseCtx.putImageData(noiseImageData, 0, 0);
// Scale up the noise to create a blotchy mask
const maskCanvas = document.createElement('canvas');
const maskCtx = maskCanvas.getContext('2d');
maskCanvas.width = canvas.width;
maskCanvas.height = canvas.height;
maskCtx.imageSmoothingEnabled = true;
maskCtx.drawImage(noiseCanvas, 0, 0, maskCanvas.width, maskCanvas.height);
const maskData = maskCtx.getImageData(0, 0, maskCanvas.width, maskCanvas.height).data;
// --- 2. Draw the original image and manipulate pixels ---
ctx.drawImage(originalImg, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
const rustThreshold = 0.6;
const dustThreshold = 0.4;
const clampedIntensity = Math.max(0, Math.min(1, intensity));
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
// --- a. Fading: Desaturate and apply a slight sepia tone ---
const gray = r * 0.299 + g * 0.587 + b * 0.114;
const desatFactor = clampedIntensity * 0.7;
r = r * (1 - desatFactor) + gray * desatFactor;
g = g * (1 - desatFactor) + gray * desatFactor;
b = b * (1 - desatFactor) + gray * desatFactor;
const sepiaFactor = clampedIntensity * 0.4;
const tr = r * 0.393 + g * 0.769 + b * 0.189;
const tg = r * 0.349 + g * 0.686 + b * 0.168;
const tb = r * 0.272 + g * 0.534 + b * 0.131;
r = r * (1 - sepiaFactor) + tr * sepiaFactor;
g = g * (1 - sepiaFactor) + tg * sepiaFactor;
b = b * (1 - sepiaFactor) + tb * sepiaFactor;
// --- b. Apply rust and dust using the noise mask ---
const maskValue = maskData[i] / 255;
// Apply Rust to bright areas of the noise mask
if (maskValue > rustThreshold) {
const rustAmount = ((maskValue - rustThreshold) / (1 - rustThreshold)) * clampedIntensity;
r = r * (1 - rustAmount) + rustRgb.r * rustAmount;
g = g * (1 - rustAmount) + rustRgb.g * rustAmount;
b = b * (1 - rustAmount) + rustRgb.b * rustAmount;
}
// Apply Dust to dark areas of the noise mask
if (maskValue < dustThreshold) {
const dustAmount = ((dustThreshold - maskValue) / dustThreshold) * clampedIntensity * 0.8;
r = r * (1 - dustAmount) + dustRgb.r * dustAmount;
g = g * (1 - dustAmount) + dustRgb.g * dustAmount;
b = b * (1 - dustAmount) + dustRgb.b * dustAmount;
}
// --- c. Add fine grain noise ---
const noise = (Math.random() - 0.5) * 255 * noiseAmount;
r += noise;
g += noise;
b += noise;
// Clamp values and update image data
data[i] = Math.max(0, Math.min(255, r));
data[i + 1] = Math.max(0, Math.min(255, g));
data[i + 2] = Math.max(0, Math.min(255, b));
}
ctx.putImageData(imageData, 0, 0);
// --- 3. Draw scratches on top ---
for (let i = 0; i < scratchCount; i++) {
const x1 = Math.random() * canvas.width;
const y1 = Math.random() * canvas.height;
const length = (Math.random() * canvas.width) / 20;
const angle = Math.random() * Math.PI * 2;
const x2 = x1 + Math.cos(angle) * length;
const y2 = y1 + Math.sin(angle) * length;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
const isLight = Math.random() > 0.5;
ctx.strokeStyle = isLight ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)';
ctx.lineWidth = Math.random() * 1.5 + 0.5;
ctx.stroke();
}
return canvas;
}
Apply Changes