You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, titleText = "ГЕРОЙ В ДОСПЕХАХ", armorColor = "silver") {
const canvas = document.createElement('canvas');
const w = originalImg.width;
const h = originalImg.height;
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
// 1. Draw original image and apply a 'heroic' cinematic effect (contrast & saturation)
ctx.filter = "contrast(1.15) saturate(1.2) brightness(1.05)";
ctx.drawImage(originalImg, 0, 0, w, h);
ctx.filter = "none";
// 2. Add an epic vignette to draw focus to the center subject (the hero)
let minDim = Math.min(w, h);
let vigGrad = ctx.createRadialGradient(w / 2, h / 2, minDim * 0.2, w / 2, h / 2, Math.max(w, h) * 0.75);
vigGrad.addColorStop(0, "rgba(0,0,0,0)");
vigGrad.addColorStop(1, "rgba(0,0,0,0.85)");
ctx.fillStyle = vigGrad;
ctx.fillRect(0, 0, w, h);
// Dynamic scale parameters based on image size
let fw = minDim * 0.08; // Main metallic frame width
let chw = minDim * 0.04; // Chainmail frame width
let trimWidth = fw * 0.15;
// 3. Define Shiny Metallic Armor Gradients
let mainColorPalette = armorColor.toLowerCase().includes("gold") ? "gold" : "silver";
let armorGrad = ctx.createLinearGradient(0, 0, w, h);
let trimGrad = ctx.createLinearGradient(w, 0, 0, h);
if (mainColorPalette === "gold") {
// Gold Armor
armorGrad.addColorStop(0, "#cc9900");
armorGrad.addColorStop(0.2, "#fff2b3");
armorGrad.addColorStop(0.35, "#e6b800");
armorGrad.addColorStop(0.48, "#664d00");
armorGrad.addColorStop(0.5, "#332600"); // Sharp reflection horizon for max "shiny"
armorGrad.addColorStop(0.52, "#b38f00");
armorGrad.addColorStop(0.75, "#ffcc00");
armorGrad.addColorStop(1, "#cc9900");
// Silver Trim
trimGrad.addColorStop(0, "#d4d4d4");
trimGrad.addColorStop(0.5, "#4a4a4a");
trimGrad.addColorStop(1, "#fcfcfc");
} else {
// Silver/Steel Armor (Default)
armorGrad.addColorStop(0, "#8a8a8a");
armorGrad.addColorStop(0.2, "#ffffff");
armorGrad.addColorStop(0.35, "#cccccc");
armorGrad.addColorStop(0.48, "#555555");
armorGrad.addColorStop(0.5, "#151515"); // Sharp reflection horizon
armorGrad.addColorStop(0.52, "#999999");
armorGrad.addColorStop(0.75, "#e0e0e0");
armorGrad.addColorStop(1, "#8a8a8a");
// Gold Trim
trimGrad.addColorStop(0, "#ffb900");
trimGrad.addColorStop(0.3, "#ffe066");
trimGrad.addColorStop(0.5, "#805a00");
trimGrad.addColorStop(0.6, "#ffcc00");
trimGrad.addColorStop(1, "#fff2b3");
}
// 4. Generate Chainmail Pattern for inner lining
let chainCanvas = document.createElement("canvas");
let cwP = Math.max(12, minDim * 0.025); // Dynamic pattern density
chainCanvas.width = cwP;
chainCanvas.height = cwP;
let cCtx = chainCanvas.getContext("2d");
// Chainmail rings design
cCtx.fillStyle = "#111"; // Backing shadow
cCtx.fillRect(0, 0, cwP, cwP);
cCtx.lineWidth = cwP * 0.15;
// First ring layer
cCtx.beginPath();
cCtx.arc(0, 0, cwP * 0.4, 0, Math.PI * 2);
cCtx.arc(cwP, 0, cwP * 0.4, 0, Math.PI * 2);
cCtx.arc(0, cwP, cwP * 0.4, 0, Math.PI * 2);
cCtx.arc(cwP, cwP, cwP * 0.4, 0, Math.PI * 2);
cCtx.strokeStyle = "#444";
cCtx.stroke();
// Overlapping ring
cCtx.beginPath();
cCtx.arc(cwP / 2, cwP / 2, cwP * 0.4, 0, Math.PI * 2);
cCtx.strokeStyle = "#888";
cCtx.stroke();
// Little shine on the ring
cCtx.beginPath();
cCtx.arc(cwP / 2, cwP / 2, cwP * 0.4, Math.PI * 1.1, Math.PI * 1.6);
cCtx.strokeStyle = "#eee";
cCtx.stroke();
let chainPattern = ctx.createPattern(chainCanvas, "repeat");
// Draw Chainmail inner frame
ctx.fillStyle = chainPattern;
ctx.beginPath();
ctx.rect(fw, fw, w - 2 * fw, h - 2 * fw); // Outer boundary of chain
ctx.rect(fw + chw, fw + chw, w - 2 * (fw + chw), h - 2 * (fw + chw)); // Inner cutout
ctx.fill("evenodd");
// Bevel shading for chainmail depth
ctx.strokeStyle = "rgba(0,0,0,0.9)";
ctx.lineWidth = chw * 0.3;
ctx.strokeRect(fw + chw, fw + chw, w - 2 * (fw + chw), h - 2 * (fw + chw));
// 5. Draw Main Shiny Body Frame Outer Layer
ctx.fillStyle = armorGrad;
ctx.shadowColor = "rgba(0,0,0,0.9)";
ctx.shadowBlur = minDim * 0.02;
ctx.beginPath();
ctx.rect(0, 0, w, h);
ctx.rect(fw, fw, w - 2 * fw, h - 2 * fw);
ctx.fill("evenodd");
ctx.shadowBlur = 0; // reset shadow
// 6. Draw Trim Boundary Border
ctx.fillStyle = trimGrad;
ctx.beginPath();
ctx.rect(fw - trimWidth, fw - trimWidth, (w - 2 * fw) + trimWidth * 2, (h - 2 * fw) + trimWidth * 2);
ctx.rect(fw, fw, w - 2 * fw, h - 2 * fw);
ctx.fill("evenodd");
// Add precise 3D bevel lines to the trim
ctx.strokeStyle = "rgba(255,255,255,0.6)";
ctx.lineWidth = Math.max(1, minDim * 0.003);
ctx.strokeRect(fw - trimWidth, fw - trimWidth, (w - 2 * fw) + trimWidth * 2, (h - 2 * fw) + trimWidth * 2);
ctx.strokeStyle = "rgba(0,0,0,0.6)";
ctx.strokeRect(fw, fw, w - 2 * fw, h - 2 * fw);
// 7. Render Layered Armor Corners Function
function drawCornerPlate(x, y, scaleX, scaleY) {
ctx.save();
ctx.translate(x, y);
ctx.scale(scaleX, scaleY);
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(fw * 2.5, 0);
ctx.lineTo(fw * 1.5, fw * 1.5);
ctx.lineTo(0, fw * 2.5);
ctx.closePath();
let plateGrad = ctx.createLinearGradient(0, 0, fw * 2.5, fw * 2.5);
if (mainColorPalette === "gold") {
plateGrad.addColorStop(0, "#fff2b3");
plateGrad.addColorStop(0.5, "#cca300");
plateGrad.addColorStop(1, "#332600");
} else {
plateGrad.addColorStop(0, "#ffffff");
plateGrad.addColorStop(0.5, "#999999");
plateGrad.addColorStop(1, "#222222");
}
ctx.shadowColor = "rgba(0,0,0,0.8)";
ctx.shadowBlur = fw * 0.2;
// Adjust shadow direction to remain strictly uniform bottom-right globally despite scaling
ctx.shadowOffsetX = scaleX * fw * 0.1;
ctx.shadowOffsetY = scaleY * fw * 0.1;
ctx.fillStyle = plateGrad;
ctx.fill();
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// Embossed trim matching plate geometry
ctx.lineWidth = fw * 0.05;
ctx.strokeStyle = trimGrad;
ctx.stroke();
ctx.restore();
}
// Apply corner reinforcements
drawCornerPlate(0, 0, 1, 1); // Top-Left
drawCornerPlate(w, 0, -1, 1); // Top-Right
drawCornerPlate(0, h, 1, -1); // Bottom-Left
drawCornerPlate(w, h, -1, -1); // Bottom-Right
// 8. Rivets Implementation
function drawRivet(rx, ry, rScale) {
let rs = rScale || minDim * 0.008;
ctx.beginPath();
ctx.arc(rx, ry, rs, 0, Math.PI * 2);
let rGrad = ctx.createRadialGradient(rx - rs * 0.4, ry - rs * 0.4, rs * 0.1, rx, ry, rs);
rGrad.addColorStop(0, "#fff");
rGrad.addColorStop(0.6, "#777");
rGrad.addColorStop(1, "#222");
ctx.fillStyle = rGrad;
ctx.fill();
ctx.strokeStyle = "rgba(0,0,0,0.8)";
ctx.lineWidth = rs * 0.3;
ctx.stroke();
}
let pw = Math.min(w * 0.6, 600); // Plaque width allocation for spacing checks
let px = w / 2 - pw / 2;
let rivetSteps = 10;
// Construct rivet grid avoiding corners and bottom plaque area
for (let i = 1; i < rivetSteps; i++) {
let rxPos = w * (i / rivetSteps);
if (rxPos > fw * 2.5 && rxPos < w - fw * 2.5) {
drawRivet(rxPos, fw * 0.5); // Top Edge
// Bottom Edge (skip plaque overlap area)
if (!(rxPos > px - fw * 0.8 && rxPos < px + pw + fw * 0.8)) {
drawRivet(rxPos, h - fw * 0.5);
}
}
}
for (let i = 1; i < rivetSteps; i++) {
let ryPos = h * (i / rivetSteps);
if (ryPos > fw * 2.5 && ryPos < h - fw * 2.5) {
drawRivet(fw * 0.5, ryPos); // Left Edge
drawRivet(w - fw * 0.5, ryPos); // Right Edge
}
}
// 9. "Hero" Plaque Construction Bottom Center
let ph = fw * 1.6;
let py = h - fw * 0.5 - ph * 0.5;
// Base Plaque Polished Outline Structure
ctx.shadowColor = "rgba(0,0,0,0.9)";
ctx.shadowBlur = minDim * 0.02;
ctx.shadowOffsetY = minDim * 0.01;
ctx.beginPath();
let chamfer = ph * 0.25;
ctx.moveTo(px + chamfer, py);
ctx.lineTo(px + pw - chamfer, py);
ctx.lineTo(px + pw, py + chamfer);
ctx.lineTo(px + pw, py + ph - chamfer);
ctx.lineTo(px + pw - chamfer, py + ph);
ctx.lineTo(px + chamfer, py + ph);
ctx.lineTo(px, py + ph - chamfer);
ctx.lineTo(px, py + chamfer);
ctx.closePath();
ctx.fillStyle = trimGrad; // Plaque body utilizes opposite trim color metal
ctx.fill();
ctx.shadowBlur = 0;
ctx.shadowOffsetY = 0;
// Inner Dark Emblazoned Recess
let inM = ph * 0.12;
ctx.beginPath();
ctx.moveTo(px + chamfer + inM, py + inM);
ctx.lineTo(px + pw - chamfer - inM, py + inM);
ctx.lineTo(px + pw - inM, py + chamfer + inM * 0.5);
ctx.lineTo(px + pw - inM, py + ph - chamfer - inM * 0.5);
ctx.lineTo(px + pw - chamfer - inM, py + ph - inM);
ctx.lineTo(px + chamfer + inM, py + ph - inM);
ctx.lineTo(px + inM, py + ph - chamfer - inM * 0.5);
ctx.lineTo(px + inM, py + chamfer + inM * 0.5);
ctx.closePath();
let darkGrad = ctx.createLinearGradient(0, py, 0, py + ph);
darkGrad.addColorStop(0, "#111");
darkGrad.addColorStop(0.5, "#3d3d3d");
darkGrad.addColorStop(1, "#050505");
ctx.fillStyle = darkGrad;
ctx.fill();
// Rivet joints fastening inner plaque
drawRivet(px + chamfer + inM * 1.6, py + ph / 2, minDim * 0.01);
drawRivet(px + pw - chamfer - inM * 1.6, py + ph / 2, minDim * 0.01);
// Render Text (e.g. ГЕРОЙ В ДОСПЕХАХ)
ctx.textAlign = "center";
ctx.textBaseline = "middle";
let fontSize = ph * 0.45;
ctx.font = `bold ${fontSize}px "Impact", "Arial Black", sans-serif`;
// Dynamic scale text font if it is too wide
let textWidthLimit = pw - (chamfer + inM * 3) * 2;
let textWidth = ctx.measureText(titleText).width;
if (textWidth > textWidthLimit) {
fontSize *= textWidthLimit / textWidth;
ctx.font = `bold ${fontSize}px "Impact", "Arial Black", sans-serif`;
}
ctx.fillStyle = armorGrad; // Blend metal gradient into phrase texture mapping
// Drop shadow simulation making phrase look embossed out of iron block
ctx.shadowColor = "black";
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.shadowBlur = 3;
ctx.fillText(titleText, w / 2, py + ph / 2 + fontSize * 0.05);
// Front high-light mapping
ctx.shadowColor = "rgba(255,255,255,0.7)";
ctx.shadowOffsetX = -1;
ctx.shadowOffsetY = -1;
ctx.shadowBlur = 1;
ctx.fillText(titleText, w / 2, py + ph / 2 + fontSize * 0.05);
ctx.shadowBlur = 0;
ctx.shadowColor = "transparent";
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// 10. Starburst & Shiny Lens Flare Functions purely emphasizing "Shiny Armor"
function drawSparkle(cx, cy, size) {
ctx.save();
ctx.translate(cx, cy);
let grad = ctx.createRadialGradient(0, 0, 0, 0, 0, size);
grad.addColorStop(0, "rgba(255,255,255,1)");
grad.addColorStop(0.2, "rgba(255,255,255,0.9)");
grad.addColorStop(1, "rgba(255,255,255,0)");
ctx.fillStyle = grad;
ctx.beginPath();
ctx.arc(0, 0, size, 0, Math.PI * 2);
ctx.fill();
// Thick ray burst
ctx.beginPath();
ctx.moveTo(-size * 2, 0); ctx.lineTo(size * 2, 0);
ctx.moveTo(0, -size * 2); ctx.lineTo(0, size * 2);
ctx.lineWidth = size * 0.15;
ctx.strokeStyle = "rgba(255,255,255,0.8)";
ctx.stroke();
// Thin sharp interior cross
ctx.beginPath();
ctx.moveTo(-size * 2, 0); ctx.lineTo(size * 2, 0);
ctx.moveTo(0, -size * 2); ctx.lineTo(0, size * 2);
ctx.lineWidth = Math.max(1, size * 0.04);
ctx.strokeStyle = "#fff";
ctx.stroke();
ctx.restore();
}
let sparkSize = minDim * 0.05;
drawSparkle(fw * 1.5, fw * 1.5, sparkSize); // Top-Left glare
drawSparkle(w - fw * 1.5, fw * 1.5, sparkSize * 0.6); // Top-Right tiny glare
drawSparkle(w - fw * 1.5, h - fw * 1.5, sparkSize); // Bottom-Right glare
// Final plaque magical sparkles
drawSparkle(px + chamfer, py + chamfer, sparkSize * 0.5);
drawSparkle(px + pw - chamfer, py + ph - chamfer, sparkSize * 0.4);
return canvas;
}
Apply Changes