You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, title = 'My Awesome Cat Video', author = 'CatLover99', views = '2,451,888 views', uploadDate = '3 days ago') {
const PLAYER_WIDTH = 1280;
const PLAYER_HEIGHT = 720;
const INFO_HEIGHT = 130;
const CANVAS_WIDTH = PLAYER_WIDTH;
const CANVAS_HEIGHT = PLAYER_HEIGHT + INFO_HEIGHT;
// Dynamically load the 'Roboto' font from Google Fonts.
// This is necessary to replicate the YouTube look and feel.
try {
const font = new FontFace('Roboto', 'url(https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu4mxK.woff2)', {
style: 'normal',
weight: '400'
});
const fontBold = new FontFace('Roboto', 'url(https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlfBBc4.woff2)', {
style: 'normal',
weight: '700'
});
await Promise.all([font.load(), fontBold.load()]);
document.fonts.add(font);
document.fonts.add(fontBold);
} catch (e) {
console.error("Roboto font could not be loaded. Using system default.", e);
}
const canvas = document.createElement('canvas');
canvas.width = CANVAS_WIDTH;
canvas.height = CANVAS_HEIGHT;
const ctx = canvas.getContext('2d');
// ===== 1. Draw Player Area =====
// Black background for the player
ctx.fillStyle = '#000000';
ctx.fillRect(0, 0, PLAYER_WIDTH, PLAYER_HEIGHT);
// Calculate image dimensions to fit inside the player (letterboxing/pillarboxing)
const imgAspectRatio = originalImg.width / originalImg.height;
const playerAspectRatio = PLAYER_WIDTH / PLAYER_HEIGHT;
let renderWidth, renderHeight, x, y;
if (imgAspectRatio > playerAspectRatio) {
// Image is wider than player
renderWidth = PLAYER_WIDTH;
renderHeight = PLAYER_WIDTH / imgAspectRatio;
x = 0;
y = (PLAYER_HEIGHT - renderHeight) / 2;
} else {
// Image is taller than or equal to player aspect ratio
renderHeight = PLAYER_HEIGHT;
renderWidth = PLAYER_HEIGHT * imgAspectRatio;
y = 0;
x = (PLAYER_WIDTH - renderWidth) / 2;
}
// Draw the main image (video thumbnail)
ctx.drawImage(originalImg, x, y, renderWidth, renderHeight);
// ===== 2. Draw Player Overlays =====
// Big central play button
const circleX = PLAYER_WIDTH / 2;
const circleY = PLAYER_HEIGHT / 2;
// Red circle background
ctx.fillStyle = 'rgba(255, 0, 0, 0.9)';
ctx.beginPath();
ctx.arc(circleX, circleY, 45, 0, Math.PI * 2);
ctx.fill();
// White play triangle
ctx.fillStyle = '#FFFFFF';
ctx.beginPath();
ctx.moveTo(circleX - 15, circleY - 22);
ctx.lineTo(circleX + 25, circleY);
ctx.lineTo(circleX - 15, circleY + 22);
ctx.closePath();
ctx.fill();
// ===== 3. Draw Control Bar =====
// A gradient at the bottom for controls to sit on
const gradient = ctx.createLinearGradient(0, PLAYER_HEIGHT - 80, 0, PLAYER_HEIGHT);
gradient.addColorStop(0, 'rgba(0,0,0,0)');
gradient.addColorStop(1, 'rgba(0,0,0,0.7)');
ctx.fillStyle = gradient;
ctx.fillRect(0, PLAYER_HEIGHT - 80, PLAYER_WIDTH, 80);
// Progress bar
const progressBarY = PLAYER_HEIGHT - 45;
ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';
ctx.fillRect(20, progressBarY, PLAYER_WIDTH - 40, 5);
ctx.fillStyle = '#FF0000';
ctx.fillRect(20, progressBarY, (PLAYER_WIDTH - 40) * 0.15, 5); // Example progress: 15%
// Small play icon (bottom left)
ctx.fillStyle = '#FFFFFF';
ctx.beginPath();
ctx.moveTo(30, PLAYER_HEIGHT - 28);
ctx.lineTo(42, PLAYER_HEIGHT - 20);
ctx.lineTo(30, PLAYER_HEIGHT - 12);
ctx.closePath();
ctx.fill();
// Volume icon
ctx.fillRect(70, PLAYER_HEIGHT - 26, 6, 12);
ctx.beginPath();
ctx.moveTo(78, PLAYER_HEIGHT - 28);
ctx.lineTo(86, PLAYER_HEIGHT - 32);
ctx.lineTo(86, PLAYER_HEIGHT - 8);
ctx.lineTo(78, PLAYER_HEIGHT - 12);
ctx.closePath();
ctx.fill();
// Timestamp
ctx.font = '16px Roboto, sans-serif';
ctx.fillText('1:34 / 10:00', 110, PLAYER_HEIGHT - 15);
// Fullscreen icon (bottom right)
const fsX = PLAYER_WIDTH - 45;
const fsY = PLAYER_HEIGHT - 30;
const fsSize = 20;
const arm = 6;
ctx.strokeStyle = '#FFFFFF';
ctx.lineWidth = 2.5;
ctx.beginPath();
ctx.moveTo(fsX, fsY + arm);
ctx.lineTo(fsX, fsY);
ctx.lineTo(fsX + arm, fsY); // Top-left
ctx.moveTo(fsX + fsSize - arm, fsY);
ctx.lineTo(fsX + fsSize, fsY);
ctx.lineTo(fsX + fsSize, fsY + arm); // Top-right
ctx.moveTo(fsX, fsY + fsSize - arm);
ctx.lineTo(fsX, fsY + fsSize);
ctx.lineTo(fsX + arm, fsY + fsSize); // Bottom-left
ctx.moveTo(fsX + fsSize - arm, fsY + fsSize);
ctx.lineTo(fsX + fsSize, fsY + fsSize);
ctx.lineTo(fsX + fsSize, fsY + fsSize - arm); // Bottom-right
ctx.stroke();
// Settings icon (cog)
const settingsX = PLAYER_WIDTH - 90;
const settingsY = PLAYER_HEIGHT - 20;
ctx.save();
ctx.translate(settingsX, settingsY);
for (let i = 0; i < 8; i++) {
ctx.rotate(Math.PI / 4);
ctx.fillRect(-1.5, -10, 3, 20);
}
ctx.restore();
ctx.beginPath();
ctx.arc(settingsX, settingsY, 5, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#000000';
ctx.beginPath();
ctx.arc(settingsX, settingsY, 2.5, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#FFFFFF'; // Reset fill style
// ===== 4. Draw Info Area =====
ctx.fillStyle = '#FFFFFF';
ctx.fillRect(0, PLAYER_HEIGHT, CANVAS_WIDTH, INFO_HEIGHT);
// Title
ctx.font = '700 28px Roboto, sans-serif';
ctx.fillStyle = '#030303';
ctx.fillText(title, 20, PLAYER_HEIGHT + 45);
// Info line (author, views, date)
const infoText = `${author} • ${views} • ${uploadDate}`;
ctx.font = '400 20px Roboto, sans-serif';
ctx.fillStyle = '#606060';
ctx.fillText(infoText, 20, PLAYER_HEIGHT + 85);
return canvas;
}
Apply Changes