You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, eventName = "ДРУЖЕСКАЯ ГОНКА", speedValue = 180, gearValue = 5, lapText = "3/3", rivalDistance = 50) {
const TARGET_W = 1280;
const TARGET_H = 720;
// Create the canvas context
const canvas = document.createElement("canvas");
canvas.width = TARGET_W;
canvas.height = TARGET_H;
const ctx = canvas.getContext("2d");
// Helper function for drawing rounded rectangles safely
function drawRoundRect(ctx, x, y, w, h, r) {
if (w < 2 * r) r = w / 2;
if (h < 2 * r) r = h / 2;
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
ctx.closePath();
}
// 1. Draw the actual original image (scaled to COVER the 16:9 canvas)
const scale = Math.max(TARGET_W / originalImg.width, TARGET_H / originalImg.height);
const w = originalImg.width * scale;
const h = originalImg.height * scale;
const x = (TARGET_W - w) / 2;
const y = (TARGET_H - h) / 2;
ctx.drawImage(originalImg, x, y, w, h);
// 2. Add an immersive vignette effect
const vignette = ctx.createRadialGradient(TARGET_W / 2, TARGET_H / 2, TARGET_H * 0.4, TARGET_W / 2, TARGET_H / 2, TARGET_W * 0.75);
vignette.addColorStop(0, "rgba(0,0,0,0)");
vignette.addColorStop(1, "rgba(0,0,0,0.6)");
ctx.fillStyle = vignette;
ctx.fillRect(0, 0, TARGET_W, TARGET_H);
// 3. Draw Checkered Flag at the top
const sqSize = 25;
for (let col = 0; col < Math.ceil(TARGET_W / sqSize); col++) {
for (let row = 0; row < 2; row++) {
ctx.fillStyle = (col + row) % 2 === 0 ? "#111" : "#eee";
ctx.fillRect(col * sqSize, row * sqSize, sqSize, sqSize);
}
}
// Add a trim under the checkered flag
ctx.fillStyle = "#ff1a1a";
ctx.fillRect(0, sqSize * 2, TARGET_W, 4);
// 4. Rear-view mirror
const mirrorW = 400;
const mirrorH = 100;
const mirrorX = (TARGET_W - mirrorW) / 2;
const mirrorY = 20;
// Mirror Frame
ctx.fillStyle = "#111";
drawRoundRect(ctx, mirrorX - 10, mirrorY - 10, mirrorW + 20, mirrorH + 20, 15);
ctx.fill();
ctx.lineWidth = 4;
ctx.strokeStyle = "#333";
ctx.stroke();
// Mirror Reflection (flipped original image)
ctx.save();
drawRoundRect(ctx, mirrorX, mirrorY, mirrorW, mirrorH, 8);
ctx.clip();
ctx.translate(canvas.width, 0);
ctx.scale(-1, 1);
ctx.drawImage(originalImg, x, y, w, h);
// Slight bluish reflection tint
ctx.fillStyle = "rgba(100, 150, 255, 0.2)";
ctx.fillRect(0, 0, TARGET_W, TARGET_H);
ctx.restore();
// 5. Draw the Dashboard Outline
const dashY = TARGET_H * 0.82;
ctx.fillStyle = "#1a1a1a";
ctx.beginPath();
ctx.moveTo(0, dashY);
ctx.lineTo(TARGET_W, dashY);
ctx.lineTo(TARGET_W, TARGET_H);
ctx.lineTo(0, TARGET_H);
ctx.closePath();
ctx.fill();
// Dash trim
ctx.beginPath();
ctx.moveTo(0, dashY);
ctx.lineTo(TARGET_W, dashY);
ctx.strokeStyle = "#e60000";
ctx.lineWidth = 6;
ctx.stroke();
// 6. Draw Dashboard Dials (Speedometer & Tachometer)
const leftDashX = TARGET_W * 0.25;
const rightDashX = TARGET_W * 0.75;
const dashCenterY = TARGET_H * 0.92;
const dialR = 85;
// --- Left Dial (Speedometer) ---
ctx.fillStyle = "#0c0c0c";
ctx.beginPath();
ctx.arc(leftDashX, dashCenterY, dialR, 0, 2 * Math.PI);
ctx.fill();
ctx.lineWidth = 6;
ctx.strokeStyle = "#444";
ctx.stroke();
// Speed markings
for(let i = 0; i <= 20; i++) {
let angle = 0.75 * Math.PI + (i / 20) * 1.5 * Math.PI;
let isMajor = i % 2 === 0;
let innerR = isMajor ? dialR * 0.75 : dialR * 0.85;
ctx.beginPath();
ctx.moveTo(leftDashX + innerR * Math.cos(angle), dashCenterY + innerR * Math.sin(angle));
ctx.lineTo(leftDashX + dialR * 0.95 * Math.cos(angle), dashCenterY + dialR * 0.95 * Math.sin(angle));
ctx.strokeStyle = i >= 16 ? "#ff4d4d" : "#ffffff";
ctx.lineWidth = isMajor ? 3 : 1;
ctx.stroke();
}
let safeSpeed = Math.min(Math.max(Number(speedValue) || 0, 0), 200);
let speedAngle = 0.75 * Math.PI + (safeSpeed / 200) * 1.5 * Math.PI;
// Needle shadow
ctx.beginPath();
ctx.moveTo(leftDashX, dashCenterY);
ctx.lineTo(leftDashX + dialR * 0.85 * Math.cos(speedAngle + 0.05), dashCenterY + dialR * 0.85 * Math.sin(speedAngle + 0.05));
ctx.strokeStyle = "rgba(0,0,0,0.5)";
ctx.lineWidth = 5;
ctx.stroke();
// Needle main
ctx.beginPath();
ctx.moveTo(leftDashX, dashCenterY);
ctx.lineTo(leftDashX + dialR * 0.85 * Math.cos(speedAngle), dashCenterY + dialR * 0.85 * Math.sin(speedAngle));
ctx.strokeStyle = "#ff1a1a";
ctx.lineWidth = 4;
ctx.stroke();
// Speedometer Center Pin
ctx.beginPath();
ctx.arc(leftDashX, dashCenterY, 12, 0, 2 * Math.PI);
ctx.fillStyle = "#111";
ctx.fill();
ctx.strokeStyle = "#555";
ctx.lineWidth = 2;
ctx.stroke();
ctx.fillStyle = "#aaa";
ctx.font = "bold 12px sans-serif";
ctx.textAlign = "center";
ctx.fillText("KM/H", leftDashX, dashCenterY + dialR * 0.5);
ctx.fillStyle = "#fff";
ctx.font = "bold 26px monospace";
ctx.fillText(String(safeSpeed), leftDashX, dashCenterY + dialR * 0.25);
// --- Right Dial (HUD info) ---
ctx.fillStyle = "#0c0c0c";
ctx.beginPath();
ctx.arc(rightDashX, dashCenterY, dialR, 0, 2 * Math.PI);
ctx.fill();
ctx.lineWidth = 6;
ctx.strokeStyle = "#444";
ctx.stroke();
ctx.fillStyle = "#00e6e6";
ctx.font = "bold 16px monospace";
ctx.textBaseline = "middle";
ctx.fillText("GEAR", rightDashX, dashCenterY - 30);
ctx.font = "bold 40px monospace";
ctx.fillStyle = "#fff";
ctx.fillText(String(gearValue), rightDashX, dashCenterY);
ctx.fillStyle = "#00e6e6";
ctx.font = "bold 14px monospace";
ctx.fillText("LAP", rightDashX, dashCenterY + 35);
ctx.fillStyle = "#fff";
ctx.font = "bold 20px monospace";
ctx.fillText(lapText, rightDashX, dashCenterY + 55);
// 7. Draw the Steering Wheel
const wcx = TARGET_W / 2;
const wcy = TARGET_H * 0.98;
const wr = 260;
// Steering wheel shadow
ctx.beginPath();
ctx.arc(wcx, wcy + 15, wr, 1.1 * Math.PI, 1.9 * Math.PI);
ctx.lineWidth = 45;
ctx.strokeStyle = "rgba(0,0,0,0.6)";
ctx.stroke();
// Wrap around steering rim
ctx.beginPath();
ctx.arc(wcx, wcy, wr, 1.1 * Math.PI, 1.9 * Math.PI);
ctx.lineWidth = 45;
ctx.strokeStyle = "#252525";
ctx.stroke();
// Inner rim details
ctx.beginPath();
ctx.arc(wcx, wcy, wr, 1.1 * Math.PI, 1.9 * Math.PI);
ctx.lineWidth = 20;
ctx.strokeStyle = "#383838";
ctx.stroke();
// Center hub
ctx.beginPath();
ctx.arc(wcx, wcy, 80, 0, 2 * Math.PI);
ctx.fillStyle = "#111";
ctx.fill();
// Wheel Spokes
ctx.fillStyle = "#1e1e1e";
// Left spoke
ctx.beginPath();
ctx.moveTo(wcx - wr + 22, wcy - 20);
ctx.lineTo(wcx - 60, wcy - 50);
ctx.lineTo(wcx - 40, wcy + 20);
ctx.lineTo(wcx - wr + 22, wcy + 50);
ctx.fill();
// Right spoke
ctx.beginPath();
ctx.moveTo(wcx + wr - 22, wcy - 20);
ctx.lineTo(wcx + 60, wcy - 50);
ctx.lineTo(wcx + 40, wcy + 20);
ctx.lineTo(wcx + wr - 22, wcy + 50);
ctx.fill();
// 8. Draw "RIVAL" Target Box to establish "Friendly Race" context
const rCx = TARGET_W * 0.55;
const rCy = TARGET_H * 0.55;
const targetSize = 25;
ctx.strokeStyle = "#ff4d4d";
ctx.lineWidth = 3;
// Target Box corners
ctx.beginPath(); ctx.moveTo(rCx - 90, rCy - 60 + targetSize); ctx.lineTo(rCx - 90, rCy - 60); ctx.lineTo(rCx - 90 + targetSize, rCy - 60); ctx.stroke();
ctx.beginPath(); ctx.moveTo(rCx + 90 - targetSize, rCy - 60); ctx.lineTo(rCx + 90, rCy - 60); ctx.lineTo(rCx + 90, rCy - 60 + targetSize); ctx.stroke();
ctx.beginPath(); ctx.moveTo(rCx - 90, rCy + 60 - targetSize); ctx.lineTo(rCx - 90, rCy + 60); ctx.lineTo(rCx - 90 + targetSize, rCy + 60); ctx.stroke();
ctx.beginPath(); ctx.moveTo(rCx + 90 - targetSize, rCy + 60); ctx.lineTo(rCx + 90, rCy + 60); ctx.lineTo(rCx + 90, rCy + 60 - targetSize); ctx.stroke();
// Target Text Background
ctx.fillStyle = "rgba(0,0,0,0.7)";
drawRoundRect(ctx, rCx - 60, rCy - 100, 120, 50, 6);
ctx.fill();
ctx.fillStyle = "#ff3333";
ctx.font = "bold 16px sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("RIVAL", rCx, rCy - 85);
ctx.fillStyle = "#fff";
ctx.fillText(`▼ ${rivalDistance}m`, rCx, rCy - 65);
// 9. Overlay Title and Game HUD Elements
// POS (Position)
ctx.fillStyle = "#FFD700";
ctx.font = "italic bold 44px Impact, sans-serif";
ctx.textAlign = "left";
ctx.fillText("POS", 40, 100);
ctx.fillStyle = "#fff";
ctx.fillText("1 / 2", 130, 100);
// Match Time
ctx.fillStyle = "#fff";
ctx.font = "bold 32px monospace";
ctx.textAlign = "right";
ctx.fillText("TIME 02:45", TARGET_W - 40, 95);
// Spectacular Arcade Title Header "ДРУЖЕСКАЯ ГОНКА"
ctx.font = "italic bold 56px Impact, sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
// Text shadow for pop
ctx.fillStyle = "rgba(0,0,0,0.8)";
ctx.fillText(eventName, TARGET_W / 2 + 5, 185);
// Gradient filling for the title
const textGrad = ctx.createLinearGradient(0, 150, 0, 210);
textGrad.addColorStop(0, "#ffffff");
textGrad.addColorStop(1, "#c0c0c0");
ctx.fillStyle = textGrad;
ctx.fillText(eventName, TARGET_W / 2, 180);
// Stroke outline for racing flair
ctx.strokeStyle = "#cc0000";
ctx.lineWidth = 3;
ctx.strokeText(eventName, TARGET_W / 2, 180);
return canvas;
}
Apply Changes