You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
mouthCenterX = "50%",
mouthCenterY = "75%",
mouthWidth = "45%",
mouthHeight = "25%",
flipDirection = "both",
featherAmount = "40"
) {
// Validate image
if (!originalImg || !originalImg.width || !originalImg.height) {
throw new Error("Invalid original image provided.");
}
// Helper to parse percentages, decimals, or absolute values
const parseVal = (val, maxVal) => {
let v = String(val).trim();
if (v.endsWith("%")) {
return (parseFloat(v) / 100) * maxVal;
}
let num = parseFloat(v);
// Treat 0.0 to 1.0 as normalized relative coordinates
if (!isNaN(num) && num > 0 && num <= 1) {
return num * maxVal;
}
return num || 0;
};
// Main dimensions
const width = originalImg.width;
const height = originalImg.height;
// Calculate mouth area
let cx = parseVal(mouthCenterX, width);
let cy = parseVal(mouthCenterY, height);
let mW = parseVal(mouthWidth, width);
let mH = parseVal(mouthHeight, height);
// Prevent zero-size canvas crashes
mW = Math.max(2, mW);
mH = Math.max(2, mH);
const mX = cx - mW / 2;
const mY = cy - mH / 2;
// Setup main canvas
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext("2d");
// Draw the untouched original image as the background layer
ctx.drawImage(originalImg, 0, 0);
// Setup temporary canvas to render and mask the backwards/flipped mouth
const mCanvas = document.createElement("canvas");
mCanvas.width = mW;
mCanvas.height = mH;
const mCtx = mCanvas.getContext("2d");
// 1. Draw the extracted mouth applying the flip transformation
mCtx.save();
let sX = 1, sY = 1;
let dir = String(flipDirection).toLowerCase();
if (dir === "horizontal" || dir === "both") sX = -1;
if (dir === "vertical" || dir === "both") sY = -1;
mCtx.translate(mW / 2, mH / 2);
mCtx.scale(sX, sY);
mCtx.translate(-mW / 2, -mH / 2);
mCtx.drawImage(originalImg, mX, mY, mW, mH, 0, 0, mW, mH);
mCtx.restore();
// 2. Erase the hard edges using an elliptical alpha mask gradient
mCtx.globalCompositeOperation = "destination-in";
mCtx.translate(mW / 2, mH / 2);
let radius;
if (mW >= mH) {
// Scale Y down to form an ellipse that fits exactly within mW x mH
mCtx.scale(1, mH / mW);
radius = mW / 2;
} else {
// Scale X down
mCtx.scale(mW / mH, 1);
radius = mH / 2;
}
// Determine feather constraints
let featherFloat = parseFloat(featherAmount) / 100 || 0;
featherFloat = Math.max(0, Math.min(1, featherFloat)); // Clamp between 0 and 1
let stopPoint = 1 - featherFloat;
// Build soft radial gradient
const grad = mCtx.createRadialGradient(0, 0, 0, 0, 0, radius);
grad.addColorStop(0, "rgba(0,0,0,1)");
if (stopPoint > 0) {
grad.addColorStop(stopPoint === 1 ? 0.999 : stopPoint, "rgba(0,0,0,1)");
}
grad.addColorStop(1, "rgba(0,0,0,0)");
mCtx.fillStyle = grad;
// Fill a rect covering beyond the radius scale to smoothly obliterate the corners
mCtx.fillRect(-radius * 2, -radius * 2, radius * 4, radius * 4);
// 3. Composite the masked, reversed mouth back into position on main canvas
ctx.drawImage(mCanvas, mX, mY);
return canvas;
}
Apply Changes