You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
focalPointXPercent = 50,
focalPointYPercent = 50,
focalRadiusPercent = 20, // Percent of smaller image dimension for the fully sharp radius
featherAmountPercent = 30, // Percent of smaller image dimension for the blur transition zone width
blurAmount = 10, // Pixel value for maximum blur
vignetteStrength = 0.3, // Strength of vignette effect (0 to 1)
dreaminess = 0.1 // Strength of dreaminess/bloom effect (0 to 1)
) {
const w = originalImg.width;
const h = originalImg.height;
// --- Step 1: Prepare base image (original or with dreaminess) ---
const baseCanvas = document.createElement('canvas');
baseCanvas.width = w;
baseCanvas.height = h;
const baseCtx = baseCanvas.getContext('2d');
baseCtx.drawImage(originalImg, 0, 0, w, h);
if (dreaminess > 0 && dreaminess <= 1) {
const bloomCanvas = document.createElement('canvas');
bloomCanvas.width = w;
bloomCanvas.height = h;
const bloomCtx = bloomCanvas.getContext('2d');
const bloomBlurAmount = Math.max(1, (blurAmount > 0 ? blurAmount : 5) * 0.25 * dreaminess);
const bloomBrightness = 1 + 0.3 * dreaminess;
bloomCtx.filter = `blur(${bloomBlurAmount}px) brightness(${bloomBrightness})`;
bloomCtx.drawImage(originalImg, 0, 0, w, h);
bloomCtx.filter = 'none';
// Attenuate original image slightly before adding bloom for better blending
baseCtx.globalAlpha = 1 - Math.min(0.7, dreaminess * 0.6);
baseCtx.drawImage(originalImg, 0, 0, w, h);
baseCtx.globalCompositeOperation = 'lighter';
baseCtx.globalAlpha = Math.min(1, dreaminess);
baseCtx.drawImage(bloomCanvas, 0, 0, w, h);
baseCtx.globalCompositeOperation = 'source-over';
baseCtx.globalAlpha = 1.0;
}
// baseCanvas now contains the (potentially) "dreamy" version of the image.
// --- Step 2: Prepare main output canvas and apply full blur if needed ---
const outputCanvas = document.createElement('canvas');
outputCanvas.width = w;
outputCanvas.height = h;
const outputCtx = outputCanvas.getContext('2d');
if (blurAmount > 0) {
// Create a fully blurred version of the base image
const blurredLayerCanvas = document.createElement('canvas');
blurredLayerCanvas.width = w;
blurredLayerCanvas.height = h;
const blurredLayerCtx = blurredLayerCanvas.getContext('2d');
blurredLayerCtx.filter = `blur(${blurAmount}px)`;
blurredLayerCtx.drawImage(baseCanvas, 0, 0, w, h);
blurredLayerCtx.filter = 'none';
outputCtx.drawImage(blurredLayerCanvas, 0, 0, w, h); // Output starts as fully blurred
} else {
outputCtx.drawImage(baseCanvas, 0, 0, w, h); // Output is just the base (dreamy or original)
}
// --- Step 3: If blur > 0, apply focused sharpness ---
// This involves drawing the sharp (or dreamy base) content selectively over the blurred background.
if (blurAmount > 0) {
const fx = w * Math.max(0, Math.min(100, focalPointXPercent)) / 100;
const fy = h * Math.max(0, Math.min(100, focalPointYPercent)) / 100;
const minDim = Math.min(w, h);
const r0 = minDim * Math.max(0, focalRadiusPercent) / 100; // Inner radius of sharpness
const featherPx = minDim * Math.max(0, featherAmountPercent) / 100;
const r1 = r0 + Math.max(1, featherPx); // Outer radius of feathering, ensure at least 1px feather width for gradient stability
// Layer with the sharp (or dreamy base) content
const sharpFocusLayerCanvas = document.createElement('canvas');
sharpFocusLayerCanvas.width = w;
sharpFocusLayerCanvas.height = h;
const sharpFocusLayerCtx = sharpFocusLayerCanvas.getContext('2d');
sharpFocusLayerCtx.drawImage(baseCanvas, 0, 0, w, h); // Content is the base image
// Mask for the focus area
const maskCanvas = document.createElement('canvas');
maskCanvas.width = w;
maskCanvas.height = h;
const maskCtx = maskCanvas.getContext('2d');
const radialGradient = maskCtx.createRadialGradient(fx, fy, r0, fx, fy, r1);
radialGradient.addColorStop(0, 'rgba(255,255,255,1)'); // Opaque white center (sharp area)
radialGradient.addColorStop(1, 'rgba(255,255,255,0)'); // Transparent edge (blurred area)
maskCtx.fillStyle = radialGradient;
maskCtx.fillRect(0, 0, w, h);
// Apply mask to the sharp layer (alpha masking)
sharpFocusLayerCtx.globalCompositeOperation = 'destination-in';
sharpFocusLayerCtx.drawImage(maskCanvas, 0, 0, w, h);
sharpFocusLayerCtx.globalCompositeOperation = 'source-over'; // Reset
// Draw the masked sharp layer onto the output (which has the blurred background)
outputCtx.drawImage(sharpFocusLayerCanvas, 0, 0, w, h);
}
// --- Step 4: Apply vignette to the output ---
if (vignetteStrength > 0 && vignetteStrength <= 1) {
const gradX = w / 2;
const gradY = h / 2;
const maxDimRadius = Math.sqrt(Math.pow(w / 2, 2) + Math.pow(h / 2, 2));
// Vignette starts softly from further in and darkens towards edges
// Stronger vignetteStrength makes the dark edge thicker and more pronounced.
const innerRadiusCoefficient = 0.7 - (vignetteStrength * 0.5); // e.g., 0.7 to 0.2
const innerRadius = maxDimRadius * Math.max(0, Math.min(1, innerRadiusCoefficient));
const outerRadius = maxDimRadius;
const vignetteGradient = outputCtx.createRadialGradient(gradX, gradY, innerRadius, gradX, gradY, outerRadius);
vignetteGradient.addColorStop(0, 'rgba(0,0,0,0)'); // Transparent in the center
vignetteGradient.addColorStop(1, `rgba(0,0,0,${Math.min(1, vignetteStrength)})`);
outputCtx.fillStyle = vignetteGradient;
outputCtx.fillRect(0, 0, w, h); // This draws the darkening gradient over existing content
}
return outputCanvas;
}
Apply Changes