You can edit the below JavaScript code to customize the image tool.
async function processImage(
originalImg,
title = "GALACTIC FRONTIERS",
author = "COSMO NOVELIST",
tagline = "A NEW ERA OF DISCOVERY",
fontName = "Orbitron, sans-serif", // Comma-separated font family list
titleFontSize = 70, // in pixels
authorFontSize = 30, // in pixels
taglineFontSize = 22, // in pixels
titleColor = "#00E0FF", // Bright cyan
authorColor = "#E0E0E0", // Light gray
taglineColor = "#B0B0B0", // Medium gray
backgroundColor = "#050015", // Very dark deep blue/purple
starColor = "#FFFFFF", // Color of stars
imageOpacity = 0.7, // Opacity of the main image, value 0 to 1
drawStars = "true", // String "true" or "false" to enable/disable starfield
titleGlowStrength = 15, // Blur radius for title glow
otherTextGlowStrength = 5 // Blur radius for other texts' glow
) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = 600;
canvas.height = 900;
const RENDER_STARS = String(drawStars).toLowerCase() === 'true';
// --- Font Loading ---
// Use a single style element to manage all dynamically loaded Google Fonts @import rules.
const dynamicFontLoaderStyleId = 'image-utility-dynamic-font-loader-style';
let styleElement = document.getElementById(dynamicFontLoaderStyleId);
if (!styleElement) {
styleElement = document.createElement('style');
styleElement.id = dynamicFontLoaderStyleId;
document.head.appendChild(styleElement);
}
// Prepare font name for Google Fonts URL (use the first font in the stack)
const primaryFontFamilyForURL = fontName.split(',')[0].trim().replace(/\s+/g, '+');
const fontImportRule = `@import url('https://fonts.googleapis.com/css2?family=${primaryFontFamilyForURL}:wght@400;700&display=swap');`;
// Add the @import rule only if it's not already present in the style element
if (!styleElement.textContent.includes(fontImportRule)) {
styleElement.textContent += fontImportRule + "\n";
}
// Format font name for canvas context (e.g., "Font Name, sans-serif")
// Ensures font names with spaces are quoted if not already.
const canvasFontNameFormatted = fontName.split(',').map(f => {
f = f.trim();
// Add quotes if font name has spaces and is not already quoted
if (f.includes(' ') && !f.startsWith('"') && !f.startsWith("'")) {
return `"${f}"`;
}
return f;
}).join(', ');
try {
// Wait for the primary font (and its weights) to be ready.
const primaryFontToLoad = fontName.split(',')[0].trim();
await Promise.all([
document.fonts.load(`400 12px "${primaryFontToLoad}"`), // Regular weight
document.fonts.load(`700 12px "${primaryFontToLoad}"`) // Bold weight
]);
} catch (err) {
console.warn(`Font loading for "${fontName.split(',')[0].trim()}" failed or timed out. Browser might use fallbacks. Error: ${err}`);
}
// --- Drawing ---
// 1. Background Color
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 2. Stars (if RENDER_STARS is true)
if (RENDER_STARS) {
_drawStarsInternal(ctx, 200, starColor, canvas.width, canvas.height); // Draw 200 stars
}
// 3. Original Image (scaled to cover canvas)
if (originalImg && originalImg.naturalWidth > 0 && originalImg.naturalHeight > 0) {
ctx.globalAlpha = Math.min(1, Math.max(0, parseFloat(imageOpacity))); // Clamp opacity
const canvasAspect = canvas.width / canvas.height;
const imgAspect = originalImg.naturalWidth / originalImg.naturalHeight;
let sx = 0, sy = 0, sWidth = originalImg.naturalWidth, sHeight = originalImg.naturalHeight;
if (imgAspect > canvasAspect) { // Image is wider than canvas, crop sides
sWidth = originalImg.naturalHeight * canvasAspect;
sx = (originalImg.naturalWidth - sWidth) / 2;
} else { // Image is taller than canvas (or same aspect), crop top/bottom
sHeight = originalImg.naturalWidth / canvasAspect;
sy = (originalImg.naturalHeight - sHeight) / 2;
}
ctx.drawImage(originalImg, sx, sy, sWidth, sHeight, 0, 0, canvas.width, canvas.height);
ctx.globalAlpha = 1.0; // Reset global alpha
} else {
// Fallback if image is missing or not loaded: dark placeholder
ctx.fillStyle = 'rgba(10,0,20,0.5)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (typeof originalImg !== 'undefined' && (!originalImg || originalImg.naturalWidth === 0)) {
ctx.fillStyle = 'rgba(255,255,255,0.7)';
ctx.textAlign = 'center';
ctx.font = '16px sans-serif';
ctx.fillText("Image loading issue", canvas.width/2, canvas.height/2);
}
}
// --- Text Elements ---
// Define font styles using the loaded (or fallback) font
const titleFullFont = `700 ${titleFontSize}px ${canvasFontNameFormatted}`;
const authorFullFont = `400 ${authorFontSize}px ${canvasFontNameFormatted}`;
const taglineFullFont = `400 ${taglineFontSize}px ${canvasFontNameFormatted}`;
// Define text positions (centered horizontally)
const centerX = canvas.width / 2;
const taglineY = canvas.height * 0.12; // Y-center for tagline block
const titleY = canvas.height * 0.25; // Y-center for title block
const authorY = canvas.height * 0.90; // Y-center for author block
// Draw Tagline
_drawMultilineTextWithGlowInternal(ctx, tagline, centerX, taglineY, taglineFullFont, taglineColor, taglineFontSize, 1.2, "center", "middle", taglineColor, otherTextGlowStrength);
// Draw Title
_drawMultilineTextWithGlowInternal(ctx, title, centerX, titleY, titleFullFont, titleColor, titleFontSize, 1.1, "center", "middle", titleColor, titleGlowStrength);
// Draw Author
_drawMultilineTextWithGlowInternal(ctx, author, centerX, authorY, authorFullFont, authorColor, authorFontSize, 1.2, "center", "middle", authorColor, otherTextGlowStrength);
return canvas;
}
// --- Helper Functions (internal to the scope of this script if copy-pasted) ---
function _drawStarsInternal(ctx, count, starColor, width, height) {
ctx.save(); // Save current context state
ctx.fillStyle = starColor;
for (let i = 0; i < count; i++) {
const x = Math.random() * width;
const y = Math.random() * height;
const radius = Math.random() * 1.2 + 0.3; // Stars of varying small sizes
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill();
}
ctx.restore(); // Restore context state
}
function _drawMultilineTextWithGlowInternal(
ctx, text, x, y,
font, color, fontSize, lineHeightFactor,
textAlign, textBaseline // textBaseline for the whole block's vertical alignment
, glowColor, glowBlur
) {
if (!text || String(text).trim() === '') return; // Do not draw if text is empty
ctx.save(); // Save current context state
ctx.font = font;
ctx.fillStyle = color;
ctx.textAlign = textAlign; // Horizontal alignment for each line
ctx.textBaseline = "middle"; // Set Canvas baseline for individual lines to middle
const lines = String(text).toUpperCase().split('\n'); // Split text by newline characters, convert to uppercase
const lineHeight = fontSize * lineHeightFactor;
// Calculate total height of the text block for vertical centering
// This accounts for the fact that textBaseline="middle" means y is middle of the line box.
const totalBlockHeight = lines.length * lineHeight;
let startY;
if (textBaseline === "middle") { // Center the entire block around y
startY = y - (totalBlockHeight / 2) + (lineHeight / 2);
} else if (textBaseline === "top") { // Align top of block with y
startY = y + (lineHeight / 2);
} else if (textBaseline === "bottom") { // Align bottom of block with y
startY = y - totalBlockHeight + (lineHeight / 2);
} else { // Default: treat y as top of the first line (if textBaseline was 'top')
startY = y + (lineHeight / 2); // Assuming y is meant for top of block
}
// Apply glow effect if specified
if (glowColor && glowBlur > 0) {
ctx.shadowColor = glowColor;
ctx.shadowBlur = glowBlur;
// For a stronger glow, you could draw the text multiple times
// or use ctx.strokeText then ctx.fillText. This is a simple shadow.
}
// Draw each line of text
for (let i = 0; i < lines.length; i++) {
ctx.fillText(lines[i], x, startY + (i * lineHeight));
}
ctx.restore(); // Restore context state (resets shadow, font, fillStyle, etc.)
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image Sci-Fi Novel Cover Template is an online tool designed to create stunning book covers for science fiction novels. Users can upload their original images and customize various elements including the title, author name, tagline, font styles, sizes, colors, and background. With support for added visual effects like starfields and text glows, this tool is perfect for authors, publishers, or anyone looking to design eye-catching covers for print or digital distribution.