You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, mistIntensity = 0.65, mistColor = '#d9e6f2') {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Set canvas dimensions to match the original image
canvas.width = originalImg.width;
canvas.height = originalImg.height;
// Parse parameters
mistIntensity = parseFloat(mistIntensity);
if (isNaN(mistIntensity) || mistIntensity < 0 || mistIntensity > 1) {
mistIntensity = 0.65; // Default fallback
}
const hexToRgb = (hex) => {
if (typeof hex !== 'string') return { r: 217, g: 230, b: 242 };
let shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function(m, r, g, b) {
return r + r + g + g + b + b;
});
let 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)
} : { r: 217, g: 230, b: 242 };
};
const color = hexToRgb(mistColor);
// 1. Draw base image
ctx.drawImage(originalImg, 0, 0);
// 2. Add an ethereal glow (mist scatters light, making highlights bloom/haze)
// We achieve this by drawing a slightly blurred version of the image softly over itself
const blurAmount = Math.min(20, Math.max(2, canvas.height * 0.015)); // Cap blur to keep performance optimal
ctx.globalAlpha = mistIntensity * 0.6;
ctx.filter = `blur(${blurAmount}px)`;
ctx.drawImage(originalImg, 0, 0);
// Reset states for overlay rendering
ctx.globalAlpha = 1.0;
ctx.filter = 'none';
// 3. Ground Mist (Screen blend for lightening effect)
// Thicker at the bottom, thinning out as it goes up
ctx.globalCompositeOperation = 'screen';
const gradient1 = ctx.createLinearGradient(0, 0, 0, canvas.height);
gradient1.addColorStop(0, `rgba(${color.r}, ${color.g}, ${color.b}, 0.0)`);
gradient1.addColorStop(0.3, `rgba(${color.r}, ${color.g}, ${color.b}, ${mistIntensity * 0.1})`);
gradient1.addColorStop(0.65, `rgba(${color.r}, ${color.g}, ${color.b}, ${mistIntensity * 0.45})`);
gradient1.addColorStop(1, `rgba(${color.r}, ${color.g}, ${color.b}, ${mistIntensity * 0.9})`);
ctx.fillStyle = gradient1;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 4. Volumetric Mist Patches
// Generating overlapping radial gradients in the bottom half gives variation and depth like actual clouds
const numPatches = 6;
for (let i = 0; i < numPatches; i++) {
// Pseudo-random placement using fractional multiplication to spread them out naturally
let px = canvas.width * ((i * 0.618) % 1);
let py = canvas.height * (0.55 + ((i * 0.732) % 0.45));
let radius = canvas.width * 0.3 + ((i * 0.414) % 0.4) * canvas.width;
let grad = ctx.createRadialGradient(px, py, 0, px, py, radius);
grad.addColorStop(0, `rgba(${color.r}, ${color.g}, ${color.b}, ${mistIntensity * 0.35})`);
grad.addColorStop(1, `rgba(${color.r}, ${color.g}, ${color.b}, 0)`);
ctx.fillStyle = grad;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
// 5. Morning Lighting Profile
// "Morning" signifies cooler shadows (blues) and warmer sunlight angles (oranges/yellows)
ctx.globalCompositeOperation = 'soft-light';
const timeOfDayGrad = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
timeOfDayGrad.addColorStop(0, `rgba(255, 235, 210, ${mistIntensity * 0.7})`); // Warm glow descending from top-left
timeOfDayGrad.addColorStop(1, `rgba(160, 190, 240, ${mistIntensity * 0.6})`); // Cold shadows clustering at bottom-right
ctx.fillStyle = timeOfDayGrad;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 6. Generic Distant Wash-Out
// Heavy mist desaturates distant views globally and slightly lifts blacks
ctx.globalCompositeOperation = 'source-over';
ctx.fillStyle = `rgba(${color.r}, ${color.g}, ${color.b}, ${mistIntensity * 0.15})`;
ctx.fillRect(0, 0, canvas.width, canvas.height);
return canvas;
}
Apply Changes