You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, text = "UNIVERSAL", subText = "PICTURES", starCount = 400) {
// Create the main output canvas
const canvas = document.createElement('canvas');
canvas.width = 800;
canvas.height = 450;
const ctx = canvas.getContext('2d');
// 1. Draw Deep Space Background
ctx.fillStyle = '#020208';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw stars with pseudo-random generation for consistency
let seed = 12345;
function random() {
seed = (seed * 9301 + 49297) % 233280;
return seed / 233280;
}
for (let i = 0; i < Number(starCount); i++) {
let x = random() * canvas.width;
let y = random() * canvas.height;
let r = random() * 1.5;
let alpha = random() * 0.7 + 0.3;
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI * 2);
ctx.fillStyle = `rgba(255, 255, 255, ${alpha})`;
ctx.fill();
}
const cx = 400;
const cy = 200;
const radius = 150;
// Background Glow around the Earth
const bgGlow = ctx.createRadialGradient(cx, cy, radius * 0.8, cx, cy, radius * 2.5);
bgGlow.addColorStop(0, 'rgba(60, 120, 255, 0.5)');
bgGlow.addColorStop(0.5, 'rgba(20, 50, 120, 0.15)');
bgGlow.addColorStop(1, 'rgba(0, 0, 0, 0)');
ctx.fillStyle = bgGlow;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Lens Flare / Sunburst (Behind the globe)
ctx.save();
ctx.globalCompositeOperation = "screen";
const sunX = cx - radius * 0.6;
const sunY = cy - radius * 0.6;
const sunGlow = ctx.createRadialGradient(sunX, sunY, 0, sunX, sunY, radius * 3);
sunGlow.addColorStop(0, "rgba(255, 255, 255, 1)");
sunGlow.addColorStop(0.1, "rgba(200, 230, 255, 0.6)");
sunGlow.addColorStop(0.3, "rgba(50, 100, 200, 0.2)");
sunGlow.addColorStop(1, "rgba(0, 0, 0, 0)");
ctx.fillStyle = sunGlow;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Light rays
ctx.translate(sunX, sunY);
ctx.fillStyle = "rgba(200, 220, 255, 0.04)";
for (let r = 0; r < 360; r += 10) {
ctx.rotate(10 * Math.PI / 180);
ctx.beginPath();
ctx.moveTo(-1, 0);
ctx.lineTo(0, -600);
ctx.lineTo(1, 0);
ctx.fill();
}
ctx.restore();
// 2. Spherize 'originalImg' to map it to the globe
const tempCanvas = document.createElement('canvas');
tempCanvas.width = 600;
tempCanvas.height = 300;
const tCtx = tempCanvas.getContext('2d');
tCtx.drawImage(originalImg, 0, 0, tempCanvas.width, tempCanvas.height);
const srcData = tCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
const d = radius * 2;
const globeCanvas = document.createElement('canvas');
globeCanvas.width = d;
globeCanvas.height = d;
const gCtx = globeCanvas.getContext('2d');
const outData = gCtx.createImageData(d, d);
for (let y = 0; y < d; y++) {
for (let x = 0; x < d; x++) {
let dx = x - radius;
let dy = y - radius;
let distSq = dx * dx + dy * dy;
if (distSq <= radius * radius) {
let z = Math.sqrt(radius * radius - distSq);
let longitude = Math.atan2(dx, z);
let latitude = Math.asin(dy / radius);
let u = (longitude / Math.PI) + 0.5;
let v = (latitude / Math.PI) + 0.5;
let sx = Math.floor(u * tempCanvas.width);
let sy = Math.floor(v * tempCanvas.height);
sx = Math.max(0, Math.min(tempCanvas.width - 1, sx));
sy = Math.max(0, Math.min(tempCanvas.height - 1, sy));
let srcIdx = (sy * tempCanvas.width + sx) * 4;
let destIdx = (y * d + x) * 4;
outData.data[destIdx] = srcData.data[srcIdx];
outData.data[destIdx + 1] = srcData.data[srcIdx + 1];
outData.data[destIdx + 2] = srcData.data[srcIdx + 2];
outData.data[destIdx + 3] = 255;
}
}
}
gCtx.putImageData(outData, 0, 0);
ctx.drawImage(globeCanvas, cx - radius, cy - radius);
// Add inner shadow and lighting to the globe to fake spherical depth
const planetShadow = ctx.createRadialGradient(cx - radius * 0.4, cy - radius * 0.4, radius * 0.1, cx, cy, radius);
planetShadow.addColorStop(0, 'rgba(255, 255, 255, 0.4)');
planetShadow.addColorStop(0.3, 'rgba(255, 255, 255, 0)');
planetShadow.addColorStop(0.7, 'rgba(0, 0, 0, 0.6)');
planetShadow.addColorStop(1, 'rgba(0, 0, 0, 0.95)');
ctx.beginPath();
ctx.arc(cx, cy, radius, 0, Math.PI * 2);
ctx.fillStyle = planetShadow;
ctx.fill();
// 3. Draw 3D Text 'UNIVERSAL'
ctx.font = "normal 900 75px 'Arial Black', Impact, sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
const chars = text.split("");
const charWidths = chars.map(c => ctx.measureText(c).width);
const totalCharWidth = charWidths.reduce((a, b) => a + b, 0);
const letterSpacing = 12;
const totalWidth = totalCharWidth + letterSpacing * (chars.length - 1);
const layers = 18; // Amount of 3D extrusion
// Draw 3D text from back layer to front layer
for (let i = layers; i >= 0; i--) {
ctx.save();
if (i === 0) {
// Front metallic face
let grad = ctx.createLinearGradient(0, cy - 20, 0, cy + 60);
grad.addColorStop(0, "#ffeaaa");
grad.addColorStop(0.3, "#e2a92d");
grad.addColorStop(0.45, "#fffdbf");
grad.addColorStop(0.55, "#b57d00");
grad.addColorStop(1, "#ffd53a");
ctx.fillStyle = grad;
ctx.shadowColor = "rgba(255, 230, 150, 0.5)";
ctx.shadowBlur = 15;
ctx.shadowOffsetY = 2;
} else {
// 3D side material (Dark gold / brown)
ctx.fillStyle = (i % 2 === 0) ? "#6b4900" : "#4a3200";
ctx.shadowBlur = (i === layers) ? 20 : 0; // Only deepest layer has dark drop shadow
ctx.shadowColor = "rgba(0,0,0,0.8)";
}
// Extrude downwards and slightly left
ctx.translate(i * -1, i * 1.5);
let startX = cx - totalWidth / 2;
let currentX = startX;
for (let j = 0; j < chars.length; j++) {
let cw = charWidths[j];
let charTargetX = currentX + cw / 2;
// Normalize X position to array (-1 to 1) relative to center
let nx = (charTargetX - cx) / (totalWidth / 2);
// Create a parabola shape so ends curve upwards mimicking perspective wrapping
let yOffset = nx * nx * -15;
ctx.save();
ctx.translate(charTargetX, cy + 50 + yOffset);
ctx.rotate(nx * 0.12);
ctx.fillText(chars[j], 0, 0);
if (i === 0) {
// Shiny bevel outline for the front face
ctx.strokeStyle = "#fffae3";
ctx.lineWidth = 1.5;
ctx.strokeText(chars[j], 0, 0);
}
ctx.restore();
currentX += cw + letterSpacing;
}
ctx.restore();
}
// 4. Draw 'PICTURES' subtitle below central logo
if (subText) {
ctx.font = "normal 900 22px 'Arial Black', Impact, sans-serif";
ctx.textAlign = "center";
const subChars = subText.split("");
const subLetterSpacing = 18;
const subCharWidths = subChars.map(c => ctx.measureText(c).width);
const subTotalWidth = subCharWidths.reduce((a, b) => a + b, 0) + subLetterSpacing * (subChars.length - 1);
let subCurX = cx - subTotalWidth / 2;
ctx.fillStyle = "#ffffff";
ctx.shadowColor = "rgba(0, 0, 0, 0.9)";
ctx.shadowBlur = 4;
for (let j = 0; j < subChars.length; j++) {
let cw = subCharWidths[j];
let cX = subCurX + cw / 2;
ctx.fillText(subChars[j], cX, cy + 140);
subCurX += cw + subLetterSpacing;
}
}
return canvas;
}
Apply Changes