You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, titleLine1 = "STRANGER THINGS", titleLine2 = "CHAPTER FIVE", glowColor = "#c81e1e") {
// Helper function to load a script dynamically
const loadScript = (src) => {
return new Promise((resolve, reject) => {
if (document.querySelector(`script[src="${src}"]`)) {
resolve();
return;
}
const script = document.createElement('script');
script.src = src;
script.onload = () => resolve();
script.onerror = () => reject(new Error(`Failed to load script: ${src}`));
script.crossOrigin = "anonymous";
document.head.appendChild(script);
});
};
// Helper function to load a font dynamically
const loadFont = (fontName, fontUrl) => {
if ([...document.fonts].some(f => f.family === fontName)) {
return document.fonts.load(`1em ${fontName}`);
}
const style = document.createElement('style');
style.textContent = `@import url('${fontUrl}');`;
document.head.appendChild(style);
return document.fonts.load(`1em ${fontName}`);
};
// Helper function to create an Image from a Base64 source
const loadImageFromBase64 = (base64) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = base64;
});
};
// --- ASSETS (BASE64 ENCODED) ---
// Mind Flayer silhouette
const mindFlayerSrc = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAAxO9DDAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAdDSURBVHgB7d1BDQAgEMCwAb/n/z2EBHfQdAvCU3K25+y9AdBafwEAEE4hAJA4hQBA4hQCAIlTCAAkTiEAkDiFAEDiFAIAiVMIACROIQCQOIUAQOIUAgCJuwbAvv1agA8d5g/wfc/r5XkRAPgX2wBgf32tAB+6z/9J9P5X6wIA8F9sA4D99bUCfOg+R8+pCwDA/7ANAJa31wsAhp3//GgAAIAAKQQAEncNgL329QIAw7C/Xu8DQIAnAAEIkEIAIHEKAQDEKQQAEqcQAECcQgAgcQoBAMQpBACQOAQAEOcAgPQCgPQAADCEAADiFIIACFMIACROIQAgTiEAEChAAIAABQCAAAUAACgAAEAABQCAAAUAACgAAEAABQCAAAUAACgAAEAABQCAAAUAACgAAEAABQCAAAUAgAAFACAABgCAAgDAAAYAgAIBAACAgAAgAIAAAAIAAIAAAIAAIAAIAAIAAAIAAIAAIAAIAAIAAIAAIAAIAAIAAAIAAIAAIAAAIAAIAAIIAECAAACBBAAAgggAAgggAAgggAAgggAAgggAAgggAAgggAAgggAAgggAAgggAAgggAAghQAABQIAAAKFAAAFCgAAChQAAFQIAECAAACBBAAAAQoAAAIEAAIEEAIEEAIEEAIEEAIEEAIEEAIEEAIEEAIEEAIEEAIEEAIEEAIEEAIEEAIEEAIEEAIEEAIEEAIIUAAAUKAAAKFAAABQoAAAoUAAAUCAAAAgQAABAEAAAECABAIAAQIAAQIAAIEDiFAIAiVMIACROIQCQOIUAQOIUAgCJUwggAJA4hQBA4hQCAIlTCAAgTiEA4BECgAF+PQCwAQwAgAAEAAIAgAAGgAF+vSDgAxgABgACAIAABgDAAAYAgAAEAAIAgAAGgAEgAIAABgDAAAYAgAAEAAIAgAAG6ANAACAABgDAAAYAgAAEAACAABgABgDAAAYAgAAEAACAABgAAgDAAAYAgAAEAACAABgAAgDAAAYgAAEAAACgAEIEAIAAIAABgDAAAYgAAEAAACgAEAAIAABgDAAAYgAAEAAACgAEIEAIAAIAABgDAAAYgAAEAAACgAEAAIAABgDAAAYIACAAACAAEDCEAADiFIIACFMIACROIQCAOIUAQKIAAQCAAAEAgAAFACAABgCAAgDAAAYAgAIBAACAQAACBAAABBIAAIIAECAAACBBAAAgggAAgggAAgggAAgggAAgggAAgggAAgggAAgggAAgggAAggQAACBAChQAAFQIAAAKFAAAChQAABQIAAAIEACAIAAQIAAQIAAQIAAQIAAQIAAQIAAQIAAQIAAQIAAQIAAQIAAQIAAQIAAQIEEKAQDEKQQAEqcQAEicQgAgcQoBAMQpBACQOAQAEOcAgPQCgPQAADCEAADiFIIACFMIACROIQBQgEIAAIAABQCAAAUAACgAAEAABQCAAAUAACgAAEAABQCAAAUAACgAAEAABQCAAAUAACgAAEAABQCAAAUAgAAEAAIAwAAGgAF+vSDgAxgABAACAAAYAwAAGgAEAAIAABgDAAAYAgAAEAAIAgAEgAIAABgDAAAYgAAEAAACgAEAAIAABgDAAAYgAAEAAACgAEAAIAABgDAAAYgAAEAAACgAEIEAIAAIAABgDAAAYgAAEAAACgAEIEAIAAIAABgDAAAYgAAEAAACAAEAAQcIIAECAAACBBAAAgggAAgggAAgggAAgggAAgggAAgggAAgggAAgggAAggggAJA4hQBA4hQCAIlTCAAkTiEAkDiFAEDiFAIAiVMIACROIQCQOAUAEOcAgPQCgPQCgPQAADCEAADiFAIAiVMIACROIQAgTiEAEDCEAADiFAIAiVMIACROIQAgTiEAEChAAIAABQCAAAUAACgAAEAABQCAAAUAACgAAEAABQCAAAUAACgAAEAABQCAAAUAACgAAEAABQCAAAUAgAAGgAEAwAAGgAFgABgABAACAAAYAwAAGgAEgAIAABgDAAAYAgAAEAAIAgAEAAIAABgDAAAYAgAAEAAIAgAHgAQwAABAEAAAgAIAABgDAAAYgAAEAAIAABgDAAAYAgAAEAAIAgAAEAIAABgDAAAYAgAAEAAIAgAAgAIABDCABAIAABgDAAAYAgAAEAAIAgAAgAFAAAIAABgDAAAYA6QUA6QEA6QUA6QEA6QEAEE4hAJA4hQBA4hQCAIlTCAAkTiEAkDiFAEDiFAIAiVMIACROIQAgQAACBAAgQAACBAAgQAACBAAgQAACBAAgQAACBAAgQAACBAAgQAACBAAgQAACBAAgQAACBDg/v/r/z8MAP//AwA3uB4/H0kN6wAAAABJRU5ErkJggg==";
// Kids on bikes silhouette
const bikesSrc = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAHgCAYAAAAxO9DDAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJTSURBVHgB7cEBDQAAAMIg+6e20BwC4gEAAAAA8MkuAACgAAAYCAAEAIAABgACAAAYAAgAAIAABoAABAAAwAAgAAAAAQwAAgAAGAAIAAADGAAEAAAAAwABgAAAAAQwAAgAAGAAIAACAAQYAAQCAAEAABwACAAAYAAgAgAEGAAEAGAAIAIAABgACAAAYAAQAACAAEAADAAIAAIAABgACAAAYAAQAACAYAAQAACAAEAADAAIAAIAABgACAADAAAAgAAEAAIAABgACAAAYAAQAAgAAEAAIAAIDBAAAACAYAAQAAgAAEAAIAAIDBAAAACAYAAQAAgAAEAAIAAGAAEAIAABAAAgAIAABgACAAAYAAQAAgAAEAAIAAIDBAAQCAAEAABgACAAAYAAQAAgAAEAAIAAADGAAEAAAAAwAB8AcAAIAABgACAAAYAAQAACBYABAAAwABgAAAAAQwAAgAAGAAIAIAABAAAgAEGAAEAAAAAwCB8AQgAAGAAEAIAABAAAgAIAABgACAAAYAAQAACBYABAAAwABgAAAAAQwAAgAAGAAIAIAABAAAgAEGAAEAAAAAwCB8AQgAAGAAEAIAABAAAgAIAABgACAAAYAAQAACBYABAAAwABgAAAAAQwAAgAAGAAIAAADGAAEAAAAAwABgAAAAAQwAAgAAGAAIAACAAQYAAQCAAEAABwACAAAYAAggAAAAQADg/g8AAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgHwEAAAAAAAAAAAAAgH8GAAAAAAAAAAAAAAD4RwEAAAAAAAAAAAAA+GcBAAAAAAAAAAAAAPhHAQAAAAAAAAAAAAD4ZwEAAAAAAAAAAAAA+EcBAAAAAAAAAAAAAPhnAQAAAAAAAAAAAAD4RwEAAAAAAAAAAAAA+GcBAAAAAAAAAAAAAPhHAQAAAAAAAAAAAAD4ZwEAAAAAAAAAAAAA+EcBAAAAAAAAAAAAAPhnAQAAAAAAAAAAAAD4RwEAAAAAAAAAAAAA+GcBAAAAAAAAAAAAAPBfAQAAAAAAAAD4LwABAAAAwCB8Acg/4gAGU3cnDQAAAABJRU5ErkJggg==";
// --- MAIN FUNCTION LOGIC ---
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
canvas.width = originalImg.naturalWidth;
canvas.height = originalImg.naturalHeight;
try {
// 1. Load all dependencies in parallel
const [
_, // Script loaded
__, // Font loaded
mindFlayerImg,
bikesImg
] = await Promise.all([
loadScript('https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation/selfie_segmentation.js'),
loadFont('Cinzel Decorative', 'https://fonts.googleapis.com/css2?family=Cinzel+Decorative:wght@700&display=swap'),
loadImageFromBase64(mindFlayerSrc),
loadImageFromBase64(bikesSrc)
]);
// 2. Initialize Selfie Segmentation model
const selfieSegmentation = new window.SelfieSegmentation({
locateFile: (file) => `https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation/${file}`
});
selfieSegmentation.setOptions({ modelSelection: 0 }); // 0 for general, 1 for landscape
// 3. Process image to get segmentation mask
const segmentationResults = await new Promise(resolve => {
selfieSegmentation.onResults(resolve);
selfieSegmentation.send({ image: originalImg });
});
// --- START DRAWING ---
// 4. Draw Background
const bgGradient = ctx.createRadialGradient(
canvas.width / 2, canvas.height, 0,
canvas.width / 2, canvas.height, canvas.height * 1.2
);
bgGradient.addColorStop(0, '#100000');
bgGradient.addColorStop(1, '#000000');
ctx.fillStyle = bgGradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 5. Draw Mind Flayer
ctx.globalAlpha = 0.25;
ctx.drawImage(mindFlayerImg, 0, -canvas.height * 0.1, canvas.width, canvas.height);
ctx.globalAlpha = 1.0;
// 6. Create masked versions of the subject
const foregroundCanvas = document.createElement('canvas');
foregroundCanvas.width = segmentationResults.segmentationMask.width;
foregroundCanvas.height = segmentationResults.segmentationMask.height;
const foregroundCtx = foregroundCanvas.getContext('2d');
// Draw original image, then use mask to clip out background
foregroundCtx.drawImage(originalImg, 0, 0);
foregroundCtx.globalCompositeOperation = 'destination-in';
foregroundCtx.drawImage(segmentationResults.segmentationMask, 0, 0, foregroundCanvas.width, foregroundCanvas.height);
// 7. Draw "Upside Down" reflection
ctx.save();
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.scale(1, -0.5); // Flip vertically and compress
ctx.translate(-canvas.width / 2, -canvas.height / 2);
ctx.filter = 'saturate(0) brightness(0.4) contrast(1.5)';
ctx.globalAlpha = 0.4;
ctx.drawImage(foregroundCanvas, 0, canvas.height * 1.5, canvas.width, canvas.height);
ctx.restore();
// 8. Draw main subject
ctx.drawImage(foregroundCanvas, 0, 0, canvas.width, canvas.height);
// 9. Draw Character Silhouettes
ctx.globalAlpha = 0.8;
ctx.fillStyle = '#000';
// Draw a solid black version first to create a simple silhouette
ctx.drawImage(bikesImg, 0, 0, canvas.width, canvas.height);
ctx.globalAlpha = 1.0;
// 10. Draw Text
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.font = `bold ${canvas.width / 12}px "Cinzel Decorative"`;
// Neon glow effect for text
ctx.fillStyle = glowColor;
ctx.shadowColor = glowColor;
ctx.shadowBlur = 20;
ctx.fillText(titleLine1, canvas.width / 2, canvas.height * 0.15);
ctx.font = `bold ${canvas.width / 20}px "Cinzel Decorative"`;
ctx.shadowBlur = 15;
ctx.fillText(titleLine2, canvas.width / 2, canvas.height * 0.25);
// Reset shadow
ctx.shadowBlur = 0;
return canvas;
} catch (error) {
console.error("Error processing image for Stranger Things theme:", error);
// Draw an error message on the canvas as a fallback
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'red';
ctx.font = '20px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('An error occurred during processing.', canvas.width / 2, canvas.height / 2);
return canvas;
}
}
Apply Changes