You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg,
frameColor = "rgb(245, 240, 230)", // A warm, aged paper color for the stamp body
pageColor = "rgb(255, 253, 250)", // A very light cream for the "page" background visible through perforations
perforationDiameter = 10, // Diameter of the perforation circles
imagePadding = 20, // Padding between the image edge and the "valley" of the perforations
postageText = "POSTAGE", // Text like "POSTAGE" usually at the top
valueText = "25c", // Text for the stamp's value, e.g., "25c", "1£"
textColor = "rgb(70, 60, 50)", // Dark, slightly desaturated brown for text
fontFamily = "'Times New Roman', Times, serif", // A classic serif font
fontSize = 14, // Desired font size for text elements
imageBorderColor = "rgba(0,0,0,0.1)", // Subtle border color around the image itself
imageBorderWidth = 1 // Width of the border around the image. Set to 0 to disable.
) {
// Ensure perforationDiameter is positive for radius calculation
const effectivePerforationDiameter = Math.max(0, perforationDiameter);
const radius = effectivePerforationDiameter / 2;
// Calculate dimensions for the stamp paper (the part that gets the frameColor)
// This is the area inside the "valleys" of the perforations.
const stampPaperWidth = originalImg.width + 2 * imagePadding;
const stampPaperHeight = originalImg.height + 2 * imagePadding;
// Canvas dimensions need to accommodate the perforations extending outwards
// The full canvas includes half of the perforation circles on its edges.
const canvasWidth = stampPaperWidth + 2 * radius;
const canvasHeight = stampPaperHeight + 2 * radius;
const canvas = document.createElement('canvas');
canvas.width = canvasWidth;
canvas.height = canvasHeight;
const ctx = canvas.getContext('2d');
// 1. Fill canvas with pageColor. This is the color seen through perforation holes
// or the color of the "sheet" the stamp is on.
ctx.fillStyle = pageColor;
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
// 2. Draw the main body of the stamp.
// This is a rectangle of frameColor, which will then be "perforated".
ctx.fillStyle = frameColor;
if (radius > 0) {
// If there are perforations, the frameColor rect is inset by `radius`.
ctx.fillRect(radius, radius, stampPaperWidth, stampPaperHeight);
} else {
// No perforations, so the frameColor fills the entire canvas.
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
}
// 3. "Punch out" the perforations by drawing circles of pageColor.
// This only happens if there's a positive perforation diameter.
if (radius > 0 && effectivePerforationDiameter > 0) {
ctx.fillStyle = pageColor; // Color for the "holes"
// Top and Bottom edges: iterate along X-axis
// Circles are centered on the edges of the stampPaperRect.
for (let curX = radius; curX <= radius + stampPaperWidth; curX += effectivePerforationDiameter) {
// Top edge punches
ctx.beginPath();
ctx.arc(curX, radius, radius, 0, 2 * Math.PI);
ctx.fill();
// Bottom edge punches
ctx.beginPath();
ctx.arc(curX, radius + stampPaperHeight, radius, 0, 2 * Math.PI);
ctx.fill();
}
// Left and Right edges: iterate along Y-axis
// Start curY from `radius + effectivePerforationDiameter` and end before `radius + stampPaperHeight`
// to avoid re-drawing corner perforations already handled by the X-axis loops.
for (let curY = radius + effectivePerforationDiameter; curY < radius + stampPaperHeight; curY += effectivePerforationDiameter) {
// Left edge punches
ctx.beginPath();
ctx.arc(radius, curY, radius, 0, 2 * Math.PI);
ctx.fill();
// Right edge punches
ctx.beginPath();
ctx.arc(radius + stampPaperWidth, curY, radius, 0, 2 * Math.PI);
ctx.fill();
}
}
// 4. Calculate image drawing position
// The image is placed inside the stampPaper area, offset by imagePadding and radius.
const imgDrawX = radius + imagePadding;
const imgDrawY = radius + imagePadding;
// 5. Draw an optional thin border around the image itself
if (imageBorderWidth > 0 && imageBorderColor && imageBorderColor !== "transparent") {
ctx.strokeStyle = imageBorderColor;
ctx.lineWidth = imageBorderWidth;
// This border is drawn on the edge of the image rectangle.
ctx.strokeRect(imgDrawX, imgDrawY, originalImg.width, originalImg.height);
}
// 6. Draw the original image onto the canvas
ctx.drawImage(originalImg, imgDrawX, imgDrawY, originalImg.width, originalImg.height);
// 7. Add vintage text elements (e.g., "POSTAGE", value)
if (postageText || valueText) {
// Calculate usable font size based on imagePadding and requested fontSize
const textHeightAllowance = imagePadding - 4; // Allow 2px margin top/bottom within padding
let finalFontSize = Math.min(fontSize, textHeightAllowance);
finalFontSize = Math.max(6, finalFontSize); // Ensure a minimum readable size (e.g., 6px)
// Only draw text if the calculated font size is reasonably large
if (finalFontSize >= 6) {
ctx.font = `${finalFontSize}px ${fontFamily}`;
ctx.fillStyle = textColor;
ctx.textAlign = "center";
ctx.textBaseline = "middle"; // Vertically center text in its line
const textCenterX = canvasWidth / 2;
if (postageText) {
// Place "POSTAGE" text in the top padding area
const postageTextY = radius + imagePadding / 2;
ctx.fillText(postageText.toUpperCase(), textCenterX, postageTextY);
}
if (valueText) {
// Place value text in the bottom padding area
const valueTextY = canvasHeight - radius - imagePadding / 2;
ctx.fillText(valueText, textCenterX, valueTextY);
}
}
}
return canvas;
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image Vintage Postage Stamp Frame Creator is a tool designed to transform any image into a visually appealing vintage postage stamp format. Users can customize various aspects of the stamp, including frame color, background color, perforation diameter, and text elements such as the type of postage and its value. This tool is ideal for creating personalized stamps for artistic projects, invitations, greeting cards, or any creative endeavor where a vintage flair is desired. It provides a simple and user-friendly interface to add a nostalgic touch to your images.