You can edit the below JavaScript code to customize the image tool.
async function processImage(
originalImg,
watermarkText = "Watermark",
fontSize = 40,
fontFamily = "Impact",
textColor = "rgba(0,0,0,0.25)",
textAngle = -45,
numBlotches = 10,
blotchBaseColor = "180,140,80", // RGB components for stain color (e.g., yellowish-brown)
blotchOpacity = 0.1, // Base opacity for the core of stain circles (they fade to transparent)
blotchSizeFactor = 0.15, // Max radius of an individual circle component within a blotch, relative to min(image_width, image_height)
agingIntensity = 0.3 // Image aging effect intensity (0=none, 1=full sepia-like)
) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const w = originalImg.width;
const h = originalImg.height;
canvas.width = w;
canvas.height = h;
// Helper to generate random number in a range
const getRandom = (min, max) => Math.random() * (max - min) + min;
// 1. Draw the original image onto the canvas
ctx.drawImage(originalImg, 0, 0, w, h);
// 2. Apply aging/sepia filter to the base image (if intensity > 0)
// This gives the overall image an older look.
if (agingIntensity > 0) {
const imageData = ctx.getImageData(0, 0, w, h);
const data = imageData.data;
const intensity = Math.max(0, Math.min(1, agingIntensity)); // Clamp intensity to [0, 1]
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
// Standard sepia tone RGB calculation
const sepiaR = Math.min(255, (r * 0.393) + (g * 0.769) + (b * 0.189));
const sepiaG = Math.min(255, (r * 0.349) + (g * 0.686) + (b * 0.168));
const sepiaB = Math.min(255, (r * 0.272) + (g * 0.534) + (b * 0.131));
// Blend original pixel color with sepia color based on intensity
data[i] = r * (1 - intensity) + sepiaR * intensity;
data[i+1] = g * (1 - intensity) + sepiaG * intensity;
data[i+2] = b * (1 - intensity) + sepiaB * intensity;
// Alpha channel (data[i+3]) remains unchanged
}
ctx.putImageData(imageData, 0, 0);
}
// 3. Apply Stains (Blotches)
// Each "blotch" is a cluster of several semi-transparent, fading circles.
const maxRadiusPerCircleComponent = Math.min(w, h) * blotchSizeFactor;
for (let i = 0; i < numBlotches; i++) {
// Determine the center point for this blotch cluster
const blotchRootX = getRandom(0, w);
const blotchRootY = getRandom(0, h);
// Number of individual circles that make up this one blotch
const numCirclesInBlotch = Math.floor(getRandom(3, 8));
// Factor to control how spread out the circles in a single blotch are
const blotchSpreadFactor = getRandom(0.5, 1.5);
for (let j = 0; j < numCirclesInBlotch; j++) {
// Radius for this individual circle component
const radius = getRandom(maxRadiusPerCircleComponent * 0.2, maxRadiusPerCircleComponent);
// Random angle and distance from the blotchRoot to position this circle component
const angle = getRandom(0, 2 * Math.PI);
// Max distance of a component circle's center from the blotchRoot.
// This defines the "spread" of the blotch.
const distanceFromRoot = getRandom(0, maxRadiusPerCircleComponent * blotchSpreadFactor * 0.5);
const circleX = blotchRootX + Math.cos(angle) * distanceFromRoot;
const circleY = blotchRootY + Math.sin(angle) * distanceFromRoot;
// Slightly vary the opacity for each circle component for a more natural look
const currentCircleOpacity = Math.max(0.01, Math.min(1, blotchOpacity * getRandom(0.7, 1.5)));
// Create a radial gradient for the stain: solid color in center, fading to transparent at edges
const grad = ctx.createRadialGradient(circleX, circleY, radius * 0.3, circleX, circleY, radius);
grad.addColorStop(0, `rgba(${blotchBaseColor}, ${currentCircleOpacity})`);
grad.addColorStop(1, `rgba(${blotchBaseColor}, 0)`); // Fade to fully transparent
ctx.fillStyle = grad;
// Draw the circle component
ctx.beginPath();
ctx.arc(circleX, circleY, radius, 0, 2 * Math.PI);
ctx.fill();
}
}
// 4. Apply Watermark Text
if (watermarkText && watermarkText.trim() !== "") {
ctx.save(); // Save current canvas state (transformations, styles)
// Position text in the center of the image
const textCenterX = w / 2;
const textCenterY = h / 2;
ctx.translate(textCenterX, textCenterY); // Move origin to canvas center
ctx.rotate(textAngle * Math.PI / 180); // Rotate canvas for angled text
// Set font properties
// Note: If `fontFamily` includes spaces or needs specific quoting (e.g., "'My Font', serif"),
// ensure the `fontFamily` string is correctly formatted.
ctx.font = `${fontSize}px ${fontFamily}`;
ctx.fillStyle = textColor;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
// Draw the watermark text at the (now) origin (0,0), which is the rotated center of the canvas
ctx.fillText(watermarkText, 0, 0);
ctx.restore(); // Restore canvas state to before text drawing
}
return canvas;
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image Watermark Stain Filter Effect Tool allows users to enhance their images by applying a customizable watermark, adding stain effects, and creating an aged appearance. This tool is ideal for photographers, graphic designers, and content creators looking to protect their images with text overlays while also giving their visuals a unique vintage feel with stain blotches. Use cases include adding personalized watermarks for branding, enhancing art photos, or creating nostalgic visuals for social media and marketing materials.