You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(
originalImg,
sepiaStrength = "0.7", // String "0.0" to "1.0" for sepia intensity
contrastReduction = "0.1", // String "0.0" to "0.5" for how much to reduce contrast
noiseIntensity = "20", // String "0" to "100" for noise amplitude
scratchCount = "10", // String "0" to "50" for number of scratches
dustParticleCount = "50", // String "0" to "200" for number of dust particles
vignetteStrength = "0.6" // String "0.0" to "1.0" for vignette opacity
) {
// Parameter parsing and validation
let pSepiaStrength = parseFloat(sepiaStrength);
if (isNaN(pSepiaStrength) || pSepiaStrength < 0 || pSepiaStrength > 1) pSepiaStrength = 0.7;
let pContrastReduction = parseFloat(contrastReduction);
if (isNaN(pContrastReduction) || pContrastReduction < 0 || pContrastReduction > 0.5) pContrastReduction = 0.1;
const contrastFactor = 1.0 - pContrastReduction;
let pNoiseIntensity = parseInt(noiseIntensity); // Integer for +/- pixel value change
if (isNaN(pNoiseIntensity) || pNoiseIntensity < 0 || pNoiseIntensity > 100) pNoiseIntensity = 20;
let pScratchCount = parseInt(scratchCount);
if (isNaN(pScratchCount) || pScratchCount < 0 || pScratchCount > 50) pScratchCount = 10;
let pDustParticleCount = parseInt(dustParticleCount);
if (isNaN(pDustParticleCount) || pDustParticleCount < 0 || pDustParticleCount > 200) pDustParticleCount = 50;
let pVignetteStrength = parseFloat(vignetteStrength);
if (isNaN(pVignetteStrength) || pVignetteStrength < 0 || pVignetteStrength > 1) pVignetteStrength = 0.6;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const w = originalImg.naturalWidth;
const h = originalImg.naturalHeight;
canvas.width = w;
canvas.height = h;
// If image is not loaded or has no dimensions, return an empty canvas
if (w === 0 || h === 0) {
// console.warn("Original image has zero width or height.");
return canvas;
}
ctx.drawImage(originalImg, 0, 0, w, h);
const imageData = ctx.getImageData(0, 0, w, h);
const data = imageData.data;
// 1. Apply Sepia, Contrast Reduction, and Noise (Pixel manipulation)
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
let finalR = r, finalG = g, finalB = b;
// Apply Sepia
if (pSepiaStrength > 0) {
const tr = 0.393 * r + 0.769 * g + 0.189 * b;
const tg = 0.349 * r + 0.686 * g + 0.168 * b;
const tb = 0.272 * r + 0.534 * g + 0.131 * b;
finalR = r * (1 - pSepiaStrength) + tr * pSepiaStrength;
finalG = g * (1 - pSepiaStrength) + tg * pSepiaStrength;
finalB = b * (1 - pSepiaStrength) + tb * pSepiaStrength;
}
// Apply Contrast Reduction
if (pContrastReduction > 0 && contrastFactor !== 1.0) {
finalR = (finalR - 127.5) * contrastFactor + 127.5;
finalG = (finalG - 127.5) * contrastFactor + 127.5;
finalB = (finalB - 127.5) * contrastFactor + 127.5;
}
// Apply Noise
if (pNoiseIntensity > 0) {
const noise = (Math.random() - 0.5) * pNoiseIntensity;
finalR += noise;
finalG += noise;
finalB += noise;
}
// Clamp values to 0-255 range
data[i] = Math.max(0, Math.min(255, finalR));
data[i + 1] = Math.max(0, Math.min(255, finalG));
data[i + 2] = Math.max(0, Math.min(255, finalB));
}
ctx.putImageData(imageData, 0, 0);
// 2. Draw Scratches
if (pScratchCount > 0) {
for (let i = 0; i < pScratchCount; i++) {
ctx.beginPath();
const isVertical = Math.random() > 0.5;
const scratchAlpha = Math.random() * 0.25 + 0.05; // Subtle alpha
const grayScale = Math.random() > 0.5 ? (30 + Math.random() * 50) : (180 + Math.random() * 50); // Dark or light gray
ctx.strokeStyle = `rgba(${grayScale}, ${grayScale}, ${grayScale}, ${scratchAlpha})`;
ctx.lineWidth = Math.random() * 1.5 + 0.3; // Thin lines
const jitterBase = Math.min(w,h) * 0.05; // Max deviation of scratch line relative to image size
if (isVertical) {
const x = Math.random() * w;
const y1 = (Math.random() * 0.5 - 0.25) * h; // Start near/off top edge
const y2 = (1 + (Math.random() * 0.5 - 0.25)) * h; // End near/off bottom edge
const jitter1 = (Math.random() - 0.5) * jitterBase;
const jitter2 = (Math.random() - 0.5) * jitterBase;
ctx.moveTo(x + jitter1, y1);
ctx.lineTo(x + jitter2, y2);
} else { // Horizontal
const y = Math.random() * h;
const x1 = (Math.random() * 0.5 - 0.25) * w; // Start near/off left edge
const x2 = (1 + (Math.random() * 0.5 - 0.25)) * w; // End near/off right edge
const jitter1 = (Math.random() - 0.5) * jitterBase;
const jitter2 = (Math.random() - 0.5) * jitterBase;
ctx.moveTo(x1, y + jitter1);
ctx.lineTo(x2, y + jitter2);
}
ctx.stroke();
}
}
// 3. Draw Dust Particles
if (pDustParticleCount > 0) {
for (let i = 0; i < pDustParticleCount; i++) {
const x = Math.random() * w;
const y = Math.random() * h;
const radius = Math.random() * 1.3 + 0.2; // Small specks (0.2px to 1.5px radius)
const alpha = Math.random() * 0.5 + 0.1; // Dust opacity
// Dust can be dark or light specks
const speckColorVal = Math.random() > 0.4 ? Math.floor(Math.random() * 70) : Math.floor(190 + Math.random() * 65); // Dark (0-69) or light (190-254)
ctx.fillStyle = `rgba(${speckColorVal}, ${speckColorVal}, ${speckColorVal}, ${alpha})`;
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill();
}
}
// 4. Draw Vignette
if (pVignetteStrength > 0) {
const centerX = w / 2;
const centerY = h / 2;
const cornerDist = Math.sqrt(Math.pow(centerX, 2) + Math.pow(centerY, 2));
// Inner radius of vignette clear area (stronger vignette means smaller clear area)
// The 0.7 and 0.5 factors control how quickly the vignette starts and its minimum size
const innerRadiusFactor = 0.7 - pVignetteStrength * 0.5;
const innerRadius = cornerDist * Math.max(0, innerRadiusFactor); // Ensure innerRadius is not negative
const gradient = ctx.createRadialGradient(
centerX, centerY, innerRadius,
centerX, centerY, cornerDist
);
// Vignette color (typically black, could be dark brown for aged effect)
const R_VIGNETTE = 20; // Dark brownish tint
const G_VIGNETTE = 10;
const B_VIGNETTE = 0;
gradient.addColorStop(0, `rgba(${R_VIGNETTE},${G_VIGNETTE},${B_VIGNETTE},0)`);
gradient.addColorStop(1, `rgba(${R_VIGNETTE},${G_VIGNETTE},${B_VIGNETTE},${pVignetteStrength})`);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, w, h);
}
return canvas;
}
Apply Changes