You can edit the below JavaScript code to customize the image tool.
async function processImage(originalImg, heartColor = '#FF0000', heartOpacity = 0.85) {
/**
* Dynamically loads the face-api.js script and required models.
* Uses a global promise to ensure the script is loaded and initialized only once.
* @returns {Promise<void>} A promise that resolves when the library is ready.
*/
const loadFaceApi = () => {
if (!window.faceApiPromise) {
window.faceApiPromise = new Promise((resolve, reject) => {
const SCRIPT_URL = 'https://cdn.jsdelivr.net/npm/@vladmandic/face-api@1.7.9/dist/face-api.min.js';
// Avoid re-adding the script if it's already in the DOM
if (document.querySelector(`script[src="${SCRIPT_URL}"]`)) {
// Script tag exists, need to check if faceapi is ready
const checkInterval = setInterval(() => {
if (window.faceapi && window.faceapi.nets.tinyFaceDetector.isLoaded) {
clearInterval(checkInterval);
resolve();
}
}, 100);
return;
}
const script = document.createElement('script');
script.src = SCRIPT_URL;
script.async = true;
script.onload = async () => {
try {
const MODEL_URL = 'https://cdn.jsdelivr.net/npm/@vladmandic/face-api@1.7.9/model';
await window.faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL);
await window.faceapi.nets.faceLandmark68TinyNet.loadFromUri(MODEL_URL);
resolve();
} catch (error) {
console.error('Error loading face-api models:', error);
reject(error);
}
};
script.onerror = (error) => {
console.error('Error loading face-api.js script:', error);
reject(new Error('Failed to load face-api.js script.'));
};
document.head.append(script);
});
}
return window.faceApiPromise;
};
/**
* Draws a heart shape centered at (0, 0) to be used with canvas transforms.
* @param {CanvasRenderingContext2D} ctx - The canvas rendering context.
* @param {number} size - The overall size of the heart.
*/
const drawHeartShape = (ctx, size) => {
const s = size; // Use size directly for width/height
ctx.beginPath();
// Move to the bottom point of the heart
ctx.moveTo(0, s / 2);
// Left curve
ctx.bezierCurveTo(s / 4, s / 4, s / 2, -s / 4, 0, -s / 2);
// Right curve
ctx.bezierCurveTo(-s / 2, -s / 4, -s / 4, s / 4, 0, s / 2);
ctx.closePath();
};
/**
* Parses a hex color string and returns RGB components and a darker stroke style.
* @param {string} hex - The hex color string (e.g., '#FF0000').
* @returns {object} An object with r, g, b values and a strokeStyle string.
*/
const parseColor = (hex) => {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
const darkR = Math.floor(r * 0.75);
const darkG = Math.floor(g * 0.75);
const darkB = Math.floor(b * 0.75);
return { r, g, b, strokeStyle: `rgb(${darkR}, ${darkG}, ${darkB})` };
};
// 1. Set up the canvas
const canvas = document.createElement('canvas');
canvas.width = originalImg.naturalWidth;
canvas.height = originalImg.naturalHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(originalImg, 0, 0);
try {
// 2. Load face detection library and models
await loadFaceApi();
// 3. Detect all faces and their landmarks in the image
const detections = await window.faceapi.detectAllFaces(originalImg, new window.faceapi.TinyFaceDetectorOptions()).withFaceLandmarks(true);
if (!detections.length) {
console.warn("Could not detect any faces in the image.");
return canvas; // Return original image if no faces are found
}
const color = parseColor(heartColor);
ctx.fillStyle = `rgba(${color.r}, ${color.g}, ${color.b}, ${heartOpacity})`;
ctx.strokeStyle = color.strokeStyle;
// 4. Iterate over each detected face to draw hearts on the eyes
detections.forEach(detection => {
const landmarks = detection.landmarks;
const drawHeartOnEye = (eyePoints) => {
// Calculate the center of the eye by averaging the points
const eyeCenter = eyePoints.reduce((acc, p) => ({ x: acc.x + p.x, y: acc.y + p.y }), { x: 0, y: 0 });
eyeCenter.x /= eyePoints.length;
eyeCenter.y /= eyePoints.length;
// Calculate eye width and add padding for heart size
const eyeWidth = Math.hypot(eyePoints[3].x - eyePoints[0].x, eyePoints[3].y - eyePoints[0].y) * 1.9;
// Calculate the angle of the eye
const eyeAngle = Math.atan2(eyePoints[3].y - eyePoints[0].y, eyePoints[3].x - eyePoints[0].x);
// Draw the heart shape
ctx.save();
ctx.translate(eyeCenter.x, eyeCenter.y);
ctx.rotate(eyeAngle);
ctx.lineWidth = eyeWidth / 12; // Make stroke proportional to the heart size
drawHeartShape(ctx, eyeWidth);
ctx.fill();
ctx.stroke();
ctx.restore();
};
drawHeartOnEye(landmarks.getLeftEye());
drawHeartOnEye(landmarks.getRightEye());
});
} catch (error) {
console.error("Failed to apply heart eyes effect due to an error:", error);
// In case of an error (e.g., script fails to load), the original image on the canvas is still returned.
}
return canvas;
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image Heart Eyes Effects Adder is a web tool designed to enhance images by adding heart-shaped overlays to the eyes of detected faces. This tool utilizes advanced face detection technology to identify facial landmarks, ensuring that heart effects are accurately placed on the eyes. Users can customize the color and opacity of the hearts to suit their preferences. This tool can be useful for creating fun and engaging images for social media posts, personalized gifts, or artistic projects where a playful touch is desired.