You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, artStyle = "series_au", character = "baymax") {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const width = originalImg.width;
const height = originalImg.height;
canvas.width = width;
canvas.height = height;
// Apply AU Series Style (Toon / Comic Filter)
const tempCanvas = document.createElement('canvas');
tempCanvas.width = width;
tempCanvas.height = height;
const tCtx = tempCanvas.getContext('2d');
// Saturation and contrast boost for an animated look
tCtx.filter = 'saturate(150%) contrast(120%)';
tCtx.drawImage(originalImg, 0, 0, width, height);
try {
const imgData = tCtx.getImageData(0, 0, width, height);
const data = imgData.data;
const step = 255 / 6; // Posterize to reduce color palette for a 2D drawn look
for (let i = 0; i < data.length; i += 4) {
data[i] = Math.round(data[i] / step) * step;
data[i+1] = Math.round(data[i+1] / step) * step;
data[i+2] = Math.round(data[i+2] / step) * step;
// Alpha (data[i+3]) remains unmodified
}
ctx.putImageData(imgData, 0, 0);
} catch (e) {
// Fallback if canvas is tainted by cross-origin policies
console.warn("Canvas tainted. Skipping cartoon posterize filter.", e);
ctx.filter = 'none';
ctx.drawImage(originalImg, 0, 0, width, height);
}
// Helper function to procedurally draw Baymax's recognizable face mask
function drawBaymaxFace(cx, cy, rx, ry) {
ctx.shadowColor = 'rgba(0, 0, 0, 0.4)';
ctx.shadowBlur = Math.max(10, width * 0.02);
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = Math.max(5, height * 0.01);
// Head base mask
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.ellipse(cx, cy, rx, ry, 0, 0, 2 * Math.PI);
ctx.fill();
ctx.shadowColor = 'transparent';
ctx.shadowBlur = 0;
ctx.shadowOffsetY = 0;
// Inner edge shading to give a polished, slightly curved helmet look
const grad = ctx.createRadialGradient(cx, cy - ry * 0.2, rx * 0.3, cx, cy, rx);
grad.addColorStop(0, "rgba(255,255,255,0)");
grad.addColorStop(0.7, "rgba(220,220,230,0.1)");
grad.addColorStop(1, "rgba(160,160,180,0.7)");
ctx.fillStyle = grad;
ctx.beginPath();
ctx.ellipse(cx, cy, rx, ry, 0, 0, 2 * Math.PI);
ctx.fill();
// Eyes (Black circles connected by a straight line)
ctx.fillStyle = '#1a1a1a';
ctx.strokeStyle = '#1a1a1a';
ctx.lineWidth = rx * 0.08;
const eyeOffsetX = rx * 0.45;
const eyeRadius = rx * 0.12;
const eyeY = cy - ry * 0.05; // Slightly offset towards the top
ctx.beginPath();
ctx.moveTo(cx - eyeOffsetX, eyeY);
ctx.lineTo(cx + eyeOffsetX, eyeY);
ctx.stroke();
ctx.beginPath();
ctx.arc(cx - eyeOffsetX, eyeY, eyeRadius, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.arc(cx + eyeOffsetX, eyeY, eyeRadius, 0, 2 * Math.PI);
ctx.fill();
// Eye Catchlights / Reflections (Animated style detail)
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.arc(cx - eyeOffsetX + eyeRadius * 0.3, eyeY - eyeRadius * 0.3, eyeRadius * 0.25, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.arc(cx + eyeOffsetX + eyeRadius * 0.3, eyeY - eyeRadius * 0.3, eyeRadius * 0.25, 0, 2 * Math.PI);
ctx.fill();
}
// Try AI Face Replacement using TensorFlow.js BlazeFace model
let facesDetected = false;
try {
if (typeof window.tf === 'undefined') {
await new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.21.0/dist/tf.min.js';
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
if (typeof window.blazeface === 'undefined') {
await new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/@tensorflow-models/blazeface@0.0.7/dist/blazeface.min.js';
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
await window.tf.ready();
const model = await window.blazeface.load();
// Detect faces (returns an array of face coordinate predictions)
const predictions = await model.estimateFaces(canvas, false);
if (predictions.length > 0) {
facesDetected = true;
for (let i = 0; i < predictions.length; i++) {
const start = predictions[i].topLeft;
const end = predictions[i].bottomRight;
const size = [end[0] - start[0], end[1] - start[1]];
const cx = start[0] + size[0] / 2;
const cy = start[1] + size[1] / 2;
// Scale face replacements slightly larger than natural face size
const rx = size[0] * 0.75;
const ry = size[1] * 0.65;
if (character.toLowerCase() === "baymax" || character.length > 0) {
drawBaymaxFace(cx, cy, rx, ry);
}
}
}
} catch (err) {
console.warn("AI Face Detection failed or was blocked. Falling back to default center overlay.", err);
}
// Fallback: If no faces were found or the AI loading failed (no internet, strict CSP)
if (!facesDetected) {
const cx = width / 2;
const cy = height * 0.45;
const rx = Math.min(width, height) * 0.35;
const ry = rx * 0.85;
drawBaymaxFace(cx, cy, rx, ry);
// Add artistic label text at the bottom
const fontSize = Math.max(20, height * 0.06);
ctx.font = `900 ${fontSize}px sans-serif`;
ctx.textAlign = 'center';
ctx.lineWidth = Math.max(2, fontSize * 0.1);
ctx.strokeStyle = '#000000';
ctx.strokeText("BIG HERO 6 AU", width / 2, height - (height * 0.08));
ctx.fillStyle = '#ffffff';
ctx.fillText("BIG HERO 6 AU", width / 2, height - (height * 0.08));
}
return canvas;
}
Apply Changes