You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, themeScale = 1.0, treasureDensity = 120) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Make sure we have valid inputs
if (!originalImg || !originalImg.width || !originalImg.height) {
canvas.width = 300;
canvas.height = 300;
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, 300, 300);
ctx.fillStyle = '#fff';
ctx.fillText('Invalid Image', 100, 150);
return canvas;
}
// Mathematical proportions for the chest
let baseDim = Math.max(originalImg.width, originalImg.height);
let P = Math.max(baseDim * 0.15 * Number(themeScale), 40); // Padding around image
let E = P * 0.6; // Frame/Chest boundary thickness
const W = originalImg.width + P * 2;
const H = originalImg.height + P * 2 + E * 1.5;
canvas.width = W;
canvas.height = H;
// --- Helper Drawing Functions ---
function getGoldGrad(ctx, x0, y0, x1, y1) {
let g = ctx.createLinearGradient(x0, y0, x1, y1);
g.addColorStop(0, '#7A5B20');
g.addColorStop(0.15, '#D2AA53');
g.addColorStop(0.5, '#FFF2B2');
g.addColorStop(0.85, '#D2AA53');
g.addColorStop(1, '#533810');
return g;
}
function drawRivet(ctx, cx, cy, r) {
ctx.fillStyle = '#2A1A05';
ctx.beginPath(); ctx.arc(cx, cy, r, 0, Math.PI*2); ctx.fill();
ctx.fillStyle = '#C29845';
ctx.beginPath(); ctx.arc(cx - r*0.2, cy - r*0.2, r*0.4, 0, Math.PI*2); ctx.fill();
}
function drawCoin(ctx, cx, cy, radius) {
ctx.beginPath();
ctx.arc(cx, cy, radius, 0, Math.PI*2);
let grad = ctx.createLinearGradient(cx - radius, cy - radius, cx + radius, cy + radius);
grad.addColorStop(0, '#FFF5C2');
grad.addColorStop(0.5, '#DDAA33');
grad.addColorStop(1, '#996611');
ctx.fillStyle = grad;
ctx.fill();
ctx.beginPath();
ctx.arc(cx, cy, radius * 0.7, 0, Math.PI*2);
ctx.lineWidth = radius * 0.1;
ctx.strokeStyle = '#A1731C';
ctx.stroke();
}
function drawJewel(ctx, cx, cy, radius, color) {
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(cx, cy - radius);
ctx.lineTo(cx + radius*0.7, cy);
ctx.lineTo(cx, cy + radius);
ctx.lineTo(cx - radius*0.7, cy);
ctx.closePath();
ctx.fill();
ctx.strokeStyle = '#000';
ctx.lineWidth = radius * 0.05;
ctx.stroke();
ctx.fillStyle = 'rgba(255,255,255,0.4)';
ctx.beginPath();
ctx.moveTo(cx, cy - radius);
ctx.lineTo(cx + radius*0.3, cy);
ctx.lineTo(cx, cy + radius*0.3);
ctx.lineTo(cx - radius*0.3, cy);
ctx.closePath();
ctx.fill();
}
// 1. Velvet Interior Background (Chest Inner Lining)
const bgRad = ctx.createRadialGradient(W/2, H/2, 0, W/2, H/2, W);
bgRad.addColorStop(0, '#D21010');
bgRad.addColorStop(0.5, '#7A0000');
bgRad.addColorStop(1, '#2E0000');
ctx.fillStyle = bgRad;
ctx.fillRect(0, 0, W, H);
// 2. Draw Original Image (Centered, stylized as the main "loot")
ctx.shadowColor = 'rgba(0,0,0,0.8)';
ctx.shadowBlur = P * 0.2;
ctx.shadowOffsetY = P * 0.1;
ctx.drawImage(originalImg, P, P);
ctx.shadowColor = 'transparent';
// Simple gold trim around original image so it blends well with the theme
ctx.strokeStyle = '#FFD700';
ctx.lineWidth = Math.max(2, P * 0.04);
ctx.strokeRect(P, P, originalImg.width, originalImg.height);
// Slight aura for magic effect
ctx.shadowColor = 'rgba(255, 215, 0, 0.4)';
ctx.shadowBlur = P * 0.3;
ctx.strokeRect(P, P, originalImg.width, originalImg.height);
ctx.shadowColor = 'transparent';
// 3. Draw Treasures (Coins and Jewels piled near the bottom inside the chest)
let safeDensity = Number(treasureDensity) || 120;
// Seedless random behavior but looks perfectly chaotic
for (let k=0; k<safeDensity; k++) {
let cx = E + Math.random() * (W - 2*E);
let cy = P + originalImg.height - (Math.random() * P * 0.8) + (Math.random() * P * 0.6);
let cr = P * 0.08 + Math.random() * P * 0.08;
drawCoin(ctx, cx, cy, cr);
}
const colors = ['#FF1493', '#00FF00', '#00BFFF', '#FF4500', '#9400D3'];
for (let j=0; j<Math.floor(safeDensity/4); j++) {
let cx = E + Math.random() * (W - 2*E);
let cy = P + originalImg.height - (Math.random() * P * 0.6) + (Math.random() * P * 0.6);
let cr = P * 0.1 + Math.random() * P * 0.05;
let col = colors[Math.floor(Math.random()*colors.length)];
drawJewel(ctx, cx, cy, cr, col);
}
// 4. Create Inward Drop Shadow (Cast by the golden frame onto the inside velvet/image/treasures)
ctx.shadowColor = 'rgba(0,0,0,0.9)';
ctx.shadowBlur = P * 0.4;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = P * 0.15;
ctx.fillStyle = '#000';
ctx.beginPath();
ctx.rect(0, 0, W, H);
ctx.rect(E, E, W - 2*E, H - 2.5*E);
ctx.fill("evenodd"); // Caster mask
ctx.shadowColor = 'transparent';
// 5. Draw the Outer Golden Chest Frame (Miters for 3D Bezel)
// Top
ctx.fillStyle = getGoldGrad(ctx, 0, 0, 0, E);
ctx.beginPath(); ctx.moveTo(0,0); ctx.lineTo(W,0); ctx.lineTo(W-E,E); ctx.lineTo(E,E); ctx.fill();
// Bottom
ctx.fillStyle = getGoldGrad(ctx, 0, H, 0, H - 1.5*E);
ctx.beginPath(); ctx.moveTo(0,H); ctx.lineTo(W,H); ctx.lineTo(W-E,H-1.5*E); ctx.lineTo(E,H-1.5*E); ctx.fill();
// Left
ctx.fillStyle = getGoldGrad(ctx, 0, 0, E, 0);
ctx.beginPath(); ctx.moveTo(0,0); ctx.lineTo(E,E); ctx.lineTo(E,H-1.5*E); ctx.lineTo(0,H); ctx.fill();
// Right
ctx.fillStyle = getGoldGrad(ctx, W, 0, W-E, 0);
ctx.beginPath(); ctx.moveTo(W,0); ctx.lineTo(W-E,E); ctx.lineTo(W-E,H-1.5*E); ctx.lineTo(W,H); ctx.fill();
// Chest Opening Slit (simulating lid separation line)
ctx.fillStyle = '#0F0802';
ctx.fillRect(0, E*0.88, W, Math.max(3, E*0.08));
// 6. Chest Rivets properly spaced across the border plates
let rr = E * 0.15; // rivet radius
for(let i=1; i<=7; i++) drawRivet(ctx, W * (i/8), E/2, rr);
for(let i=1; i<=7; i++) drawRivet(ctx, W * (i/8), H - E*0.75, rr);
for(let i=1; i<=7; i++) drawRivet(ctx, E/2, H * (i/8), rr);
for(let i=1; i<=7; i++) drawRivet(ctx, W - E/2, H * (i/8), rr);
// 7. Corner Reinforcement Braces
function drawCorner(cx, cy, sX, sY) {
const L1 = E * 1.8;
const L2 = E * 0.6;
ctx.fillStyle = '#4A2D0D'; // Dark bronze
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.lineTo(cx + sX*L1, cy);
ctx.lineTo(cx + sX*L1, cy + sY*L2);
ctx.lineTo(cx + sX*L2, cy + sY*L2);
ctx.lineTo(cx + sX*L2, cy + sY*L1);
ctx.lineTo(cx, cy + sY*L1);
ctx.closePath();
ctx.fill();
ctx.strokeStyle = '#BF903A';
ctx.lineWidth = Math.max(2, E*0.03);
ctx.stroke();
drawRivet(ctx, cx + sX*L2*0.5, cy + sY*L2*0.5, E*0.12);
}
drawCorner(0, 0, 1, 1); // TL
drawCorner(W, 0, -1, 1); // TR
drawCorner(0, H, 1, -1); // BL
drawCorner(W, H, -1, -1); // BR
// 8. The Chest Lock (Front & Center!)
const lw = E * 1.8;
const lh = E * 2.4;
const lx = W/2 - lw/2;
const ly = H - 1.5*E - lh*0.45;
// Lock Plate Shadow
ctx.shadowColor = 'rgba(0,0,0,0.8)';
ctx.shadowBlur = 10;
ctx.shadowOffsetY = 5;
// Lock base shape
ctx.fillStyle = getGoldGrad(ctx, lx, ly, lx+lw, ly);
ctx.beginPath();
const lr = lw/2;
ctx.arc(lx + lr, ly + lr, lr, Math.PI, 0); // Arch top
ctx.lineTo(lx + lw, ly + lh);
ctx.lineTo(lx, ly + lh);
ctx.closePath();
ctx.fill();
ctx.shadowColor = 'transparent';
// Lock Outline
ctx.strokeStyle = '#3D2505';
ctx.lineWidth = Math.max(2, E*0.05);
ctx.stroke();
// Keyhole
ctx.fillStyle = '#050302';
const kx = lx + lw/2;
const ky = ly + lr*1.2;
const kr = lw*0.14;
ctx.beginPath();
ctx.arc(kx, ky, kr, 0, Math.PI*2);
ctx.moveTo(kx - kr*0.8, ky);
ctx.lineTo(kx + kr*0.8, ky);
ctx.lineTo(kx + kr*1.3, ky + kr*3);
ctx.lineTo(kx - kr*1.3, ky + kr*3);
ctx.fill();
// Lock Screws
[
[lx + lw*0.2, ly + lh*0.85],
[lx + lw*0.8, ly + lh*0.85],
[lx + lw*0.2, ly + lr*0.8],
[lx + lw*0.8, ly + lr*0.8]
].forEach(([rvx, rvy]) => drawRivet(ctx, rvx, rvy, lw*0.06));
return canvas;
}
Apply Changes