You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, aspectRatioString = '16:9') {
let targetRatio = 16 / 9;
try {
const parts = String(aspectRatioString).split(':');
if (parts.length === 2 && !isNaN(parts[0]) && !isNaN(parts[1])) {
const num = parseFloat(parts[0]);
const den = parseFloat(parts[1]);
if (den > 0 && num > 0) {
targetRatio = num / den;
}
}
} catch (e) {
console.warn('Invalid aspect ratio format, defaulting to 16:9');
}
const imgRatio = originalImg.width / originalImg.height;
let targetWidth, targetHeight;
if (imgRatio > targetRatio) {
// Image is wider than target ratio
targetHeight = originalImg.height;
targetWidth = Math.round(originalImg.height * targetRatio);
} else {
// Image is taller than target ratio (likely a long image)
targetWidth = originalImg.width;
targetHeight = Math.round(originalImg.width / targetRatio);
}
async function loadScript(url, globalName) {
if (window[globalName]) return window[globalName];
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = url;
script.onload = () => resolve(window[globalName]);
script.onerror = reject;
document.head.appendChild(script);
});
}
let faces = [];
// Approach 1: Try Native FaceDetector (Shape Detection API)
if ('FaceDetector' in window) {
try {
const detector = new window.FaceDetector();
const detected = await detector.detect(originalImg);
faces = detected.map(f => ({
x: f.boundingBox.x,
y: f.boundingBox.y,
width: f.boundingBox.width,
height: f.boundingBox.height
}));
} catch (e) {
console.warn('FaceDetector not available or failed', e);
}
}
// Approach 2: Fallback to tracking.js for face detection
if (faces.length === 0) {
try {
await loadScript('https://cdnjs.cloudflare.com/ajax/libs/tracking.js/1.1.3/tracking-min.js', 'tracking');
await new Promise((resolve, reject) => {
if (window.tracking.ViolaJones && window.tracking.ViolaJones.classifiers && window.tracking.ViolaJones.classifiers.face) {
resolve();
} else {
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/tracking.js/1.1.3/data/face-min.js';
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
}
});
// Prevent UI freeze on large images by downscaling the detection phase
const MAX_DIM = 1200;
let scaleFactor = 1;
if (originalImg.width > MAX_DIM || originalImg.height > MAX_DIM) {
scaleFactor = MAX_DIM / Math.max(originalImg.width, originalImg.height);
}
const trackWidth = Math.round(originalImg.width * scaleFactor);
const trackHeight = Math.round(originalImg.height * scaleFactor);
const trackingCanvas = document.createElement('canvas');
trackingCanvas.width = trackWidth;
trackingCanvas.height = trackHeight;
const trackingCtx = trackingCanvas.getContext('2d');
trackingCtx.drawImage(originalImg, 0, 0, trackWidth, trackHeight);
// Manually fetch pixels to handle cross-origin canvas taint cleanly
const imageData = trackingCtx.getImageData(0, 0, trackWidth, trackHeight);
const tracker = new window.tracking.ObjectTracker('face');
tracker.setInitialScale(4);
tracker.setStepSize(2);
tracker.setEdgesDensity(0.1);
const trackedFaces = await new Promise(resolve => {
const timeout = setTimeout(() => resolve([]), 5000);
tracker.on('track', event => {
clearTimeout(timeout);
resolve(event.data);
});
tracker.track(imageData.data, trackWidth, trackHeight);
});
if (trackedFaces) {
// Scale coordinates back to original size
faces = trackedFaces.map(f => ({
x: Math.round(f.x / scaleFactor),
y: Math.round(f.y / scaleFactor),
width: Math.round(f.width / scaleFactor),
height: Math.round(f.height / scaleFactor)
}));
}
} catch (e) {
console.warn('Tracking.js face detection failed or canvas is tainted due to cross-origin isolation.', e);
}
}
const boost = faces.map(f => ({
x: f.x,
y: f.y,
width: f.width,
height: f.height,
weight: 1.0 // Emphasize facial areas strongly
}));
// Approach 3: Apply smartcrop.js to find the most visually significant 16:9 segment
await loadScript('https://cdnjs.cloudflare.com/ajax/libs/smartcrop/2.0.5/smartcrop.min.js', 'smartcrop');
// Passing the actual dimension to force maximum scale sliding (avoids zoom-in crops entirely)
const result = await window.smartcrop.crop(originalImg, {
width: Math.max(1, targetWidth),
height: Math.max(1, targetHeight),
boost: boost
});
const crop = result.topCrop;
const canvas = document.createElement('canvas');
canvas.width = targetWidth;
canvas.height = targetHeight;
const ctx = canvas.getContext('2d');
// Improve final sampling quality
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, targetWidth, targetHeight);
ctx.drawImage(
originalImg,
crop.x, crop.y, crop.width, crop.height,
0, 0, targetWidth, targetHeight
);
return canvas;
}
Apply Changes