You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
milkOverlayOpacity = 0.2,
imageBaseBrightness = 1.2,
imageBaseSaturation = 0.7,
splashElementOpacity = 0.75,
splashDensityFactor = 1.0,
splashSizeFactor = 1.0
) {
/**
* Helper function to draw procedural milk splashes on the canvas.
* @param {CanvasRenderingContext2D} ctx - The canvas context to draw on.
* @param {number} width - The width of the area to draw splashes.
* @param {number} height - The height of the area to draw splashes.
* @param {number} densityFactor - Multiplier for the number of splash groups.
* @param {number} sizeFactor - Multiplier for the size of splashes.
*/
function drawProceduralSplash(ctx, width, height, densityFactor, sizeFactor) {
const baseNumSplatGroups = 8;
const referenceDimension = 500;
const imageAreaScaleFactor = Math.sqrt((width * height) / (referenceDimension * referenceDimension));
const numSplatGroups = Math.max(1, Math.floor(baseNumSplatGroups * densityFactor * imageAreaScaleFactor));
for (let i = 0; i < numSplatGroups; i++) {
const centerX = Math.random() * width;
const centerY = Math.random() * height;
const baseRadiusSplatGroup = (Math.random() * 0.04 + 0.02) * Math.min(width, height) * sizeFactor;
// If the potential splash group is too small to be visible, skip it.
// This check helps especially if sizeFactor is very small.
if (baseRadiusSplatGroup < 0.5) continue;
const numBlobsInSplat = 3 + Math.floor(Math.random() * 4); // 3 to 6 blobs
for(let j = 0; j < numBlobsInSplat; j++) {
const angle = Math.random() * Math.PI * 2;
const distFromCenter = Math.random() * baseRadiusSplatGroup * 0.6;
const blobX = centerX + Math.cos(angle) * distFromCenter;
const blobY = centerY + Math.sin(angle) * distFromCenter;
const blobRadius = Math.max(1, baseRadiusSplatGroup * (0.5 + Math.random() * 0.7));
ctx.beginPath();
ctx.arc(blobX, blobY, blobRadius, 0, Math.PI * 2);
const splatBrightness = 240 + Math.random() * 15;
ctx.fillStyle = `rgb(${splatBrightness}, ${splatBrightness}, ${splatBrightness})`;
ctx.fill();
}
const numDroplets = Math.floor((5 + Math.random() * 10) * Math.max(0.5, sizeFactor) );
for (let k = 0; k < numDroplets; k++) {
const angle = Math.random() * Math.PI * 2;
const dist = baseRadiusSplatGroup * (0.8 + Math.random() * 2.5);
const dropletX = centerX + Math.cos(angle) * dist;
const dropletY = centerY + Math.sin(angle) * dist;
const dropletRadius = baseRadiusSplatGroup * (0.05 + Math.random() * 0.25);
if (dropletRadius >= 1) {
ctx.beginPath();
ctx.arc(dropletX, dropletY, dropletRadius, 0, Math.PI * 2);
const dropletBrightness = 220 + Math.random() * 35;
ctx.fillStyle = `rgb(${dropletBrightness}, ${dropletBrightness}, ${dropletBrightness})`;
ctx.fill();
}
}
}
}
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
milkOverlayOpacity = Math.max(0, Math.min(1, Number(milkOverlayOpacity)));
imageBaseBrightness = Math.max(0, Number(imageBaseBrightness)); // Brightness can be > 1
imageBaseSaturation = Math.max(0, Number(imageBaseSaturation)); // Saturation can be > 1
splashElementOpacity = Math.max(0, Math.min(1, Number(splashElementOpacity)));
splashDensityFactor = Math.max(0, Number(splashDensityFactor));
splashSizeFactor = Math.max(0, Number(splashSizeFactor));
const imgWidth = originalImg.naturalWidth || originalImg.width;
const imgHeight = originalImg.naturalHeight || originalImg.height;
if (!imgWidth || !imgHeight) {
console.error("Milk Splash Filter: Image dimensions are not available. Ensure the image is loaded.");
// Return an empty (0x0) canvas if image dimensions are invalid
canvas.width = 0;
canvas.height = 0;
return canvas;
}
canvas.width = imgWidth;
canvas.height = imgHeight;
// 1. Apply base image effects (brightness, saturation) and draw the image
const filters = [];
if (imageBaseBrightness !== 1) filters.push(`brightness(${imageBaseBrightness})`);
if (imageBaseSaturation !== 1) filters.push(`saturate(${imageBaseSaturation})`);
if (filters.length > 0) {
ctx.filter = filters.join(' ');
ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
ctx.filter = 'none'; // Reset filter
} else {
ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
}
// 2. Apply milky overlay
if (milkOverlayOpacity > 0) {
ctx.fillStyle = `rgba(255, 255, 255, ${milkOverlayOpacity})`;
ctx.fillRect(0, 0, imgWidth, imgHeight);
}
// 3. Draw procedural splashes
if (splashElementOpacity > 0 && splashDensityFactor > 0 && splashSizeFactor > 0) {
ctx.globalAlpha = splashElementOpacity;
drawProceduralSplash(ctx, imgWidth, imgHeight, splashDensityFactor, splashSizeFactor);
ctx.globalAlpha = 1.0; // Reset globalAlpha
}
return canvas;
}
Apply Changes