You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, heartScale = 0.85, cardboardColor = "#b58b59", shadowAlpha = 0.65) {
// 1. Initialize Canvas
const canvas = document.createElement('canvas');
const w = originalImg.width;
const h = originalImg.height;
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
// Parse parameters to correct types
const scale = Number(heartScale) || 0.85;
const alpha = Number(shadowAlpha) || 0.65;
const minDim = Math.min(w, h);
const heartSize = minDim * scale;
// 2. Draw original image onto the main canvas
ctx.drawImage(originalImg, 0, 0, w, h);
// Apply a subtle warm vintage filter to the photo for the "love" aesthetic
ctx.fillStyle = 'rgba(255, 180, 150, 0.15)';
ctx.fillRect(0, 0, w, h);
// 3. Create Cardboard Layer Canvas
const cbCanvas = document.createElement('canvas');
cbCanvas.width = w;
cbCanvas.height = h;
const cbCtx = cbCanvas.getContext('2d');
// Fill base cardboard color
cbCtx.fillStyle = cardboardColor;
cbCtx.fillRect(0, 0, w, h);
// Generate Corrugated Cardboard Texture (Vertical lines)
cbCtx.lineWidth = Math.max(1, minDim * 0.002);
const gap = Math.max(4, minDim * 0.015);
// Dark ridges
cbCtx.strokeStyle = 'rgba(0, 0, 0, 0.06)';
for (let i = 0; i < w; i += gap) {
cbCtx.beginPath();
cbCtx.moveTo(i, 0);
cbCtx.lineTo(i, h);
cbCtx.stroke();
}
// Highlighting ridges for 3D corrugated effect
cbCtx.strokeStyle = 'rgba(255, 255, 255, 0.06)';
for (let i = gap / 2; i < w; i += gap) {
cbCtx.beginPath();
cbCtx.moveTo(i, 0);
cbCtx.lineTo(i, h);
cbCtx.stroke();
}
// Add noise to simulate cardboard pulp and texture
const numNoiseScratches = Math.floor((w * h) / 1200);
cbCtx.fillStyle = 'rgba(0, 0, 0, 0.08)';
for (let i = 0; i < numNoiseScratches; i++) {
cbCtx.fillRect(Math.random() * w, Math.random() * h, 1 + Math.random() * 2, 1 + Math.random() * 2);
}
cbCtx.fillStyle = 'rgba(255, 255, 255, 0.08)';
for (let i = 0; i < numNoiseScratches; i++) {
cbCtx.fillRect(Math.random() * w, Math.random() * h, 1 + Math.random() * 2, 1 + Math.random() * 2);
}
// 4. Cut the Heart-shaped Hole
function createHeartPath(context, cx, cy, size) {
context.beginPath();
let hw = size * 0.43;
let hh = size * 0.43;
context.moveTo(cx, cy - hh * 0.3); // Top dip
// Right half of heart
context.bezierCurveTo(
cx + hw * 0.9, cy - hh * 0.9,
cx + hw * 1.5, cy + hh * 0.2,
cx, cy + hh * 1.1
); // Bottom point
// Left half of heart
context.bezierCurveTo(
cx - hw * 1.5, cy + hh * 0.2,
cx - hw * 0.9, cy - hh * 0.9,
cx, cy - hh * 0.3
); // Back to top dip
}
cbCtx.globalCompositeOperation = 'destination-out';
createHeartPath(cbCtx, w / 2, h / 2, heartSize);
cbCtx.fill();
cbCtx.globalCompositeOperation = 'source-over';
// Add raw corrugated inner edge details to the cutout heart hole
cbCtx.strokeStyle = 'rgba(0, 0, 0, 0.12)';
cbCtx.lineWidth = gap * 0.4;
cbCtx.stroke();
cbCtx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
cbCtx.lineWidth = gap * 0.15;
cbCtx.stroke();
// 5. Add "Cardboard Love" Artistic Touches (Tape & Marker Scribbles)
function drawTape(x, y, tw, th, angle) {
cbCtx.save();
cbCtx.translate(x, y);
cbCtx.rotate(angle);
// Masking tape base
cbCtx.fillStyle = 'rgba(230, 220, 190, 0.85)';
cbCtx.fillRect(-tw / 2, -th / 2, tw, th);
// Tape wrinkles
cbCtx.strokeStyle = 'rgba(255, 255, 255, 0.3)';
cbCtx.lineWidth = Math.max(1, tw * 0.02);
cbCtx.beginPath(); cbCtx.moveTo(-tw / 2, -th / 4); cbCtx.lineTo(tw / 2, -th / 4); cbCtx.stroke();
cbCtx.beginPath(); cbCtx.moveTo(-tw / 2, th / 3); cbCtx.lineTo(tw / 2, th / 3); cbCtx.stroke();
cbCtx.restore();
}
const tapeW = minDim * 0.25;
const tapeH = minDim * 0.06;
drawTape(w * 0.1, h * 0.1, tapeW, tapeH, -Math.PI / 4.5);
drawTape(w * 0.9, h * 0.1, tapeW, tapeH, Math.PI / 3.8);
drawTape(w * 0.1, h * 0.9, tapeW, tapeH, Math.PI / 4.2);
drawTape(w * 0.9, h * 0.9, tapeW, tapeH, -Math.PI / 5);
function scribbleMarkerHeart(x, y, size, angle) {
cbCtx.save();
cbCtx.translate(x, y);
cbCtx.rotate(angle);
cbCtx.strokeStyle = 'rgba(220, 30, 40, 0.85)'; // Red marker color
cbCtx.lineWidth = Math.max(2, size * 0.1);
cbCtx.lineCap = 'round';
cbCtx.lineJoin = 'round';
// Marker pass 1
cbCtx.beginPath();
cbCtx.moveTo(0, size * 0.2);
cbCtx.bezierCurveTo(size * 0.5, -size * 0.3, size * 1.2, size * 0.3, 0, size);
cbCtx.bezierCurveTo(-size * 1.2, size * 0.3, -size * 0.5, -size * 0.3, 0, size * 0.2);
cbCtx.stroke();
// Marker pass 2 (for scribble look)
cbCtx.beginPath();
cbCtx.moveTo(-size * 0.05, size * 0.25);
cbCtx.bezierCurveTo(size * 0.45, -size * 0.25, size * 1.15, size * 0.35, 0, size * 1.05);
cbCtx.bezierCurveTo(-size * 1.15, size * 0.25, -size * 0.55, -size * 0.25, -size * 0.05, size * 0.25);
cbCtx.stroke();
cbCtx.restore();
}
scribbleMarkerHeart(w * 0.18, h * 0.25, minDim * 0.09, -0.3);
scribbleMarkerHeart(w * 0.82, h * 0.72, minDim * 0.07, 0.4);
scribbleMarkerHeart(w * 0.85, h * 0.35, minDim * 0.05, 0.15);
scribbleMarkerHeart(w * 0.22, h * 0.8, minDim * 0.055, -0.2);
// 6. Final Compositing
// Set a drop shadow on the main canvas context.
// By drawing the cardboard cutout over the original image, inner shadow will be cast inside the heart hole realistically.
ctx.shadowColor = `rgba(0, 0, 0, ${alpha})`;
ctx.shadowBlur = minDim * 0.035;
ctx.shadowOffsetX = minDim * 0.01;
ctx.shadowOffsetY = minDim * 0.015;
// Print the prepared cardboard layer
ctx.drawImage(cbCanvas, 0, 0);
// Reset shadow
ctx.shadowColor = 'transparent';
return canvas;
}
Apply Changes