You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, tailLengthScale = 0.2, tailAngle = -45, tailColor = "#88ccff", drawTargetHUD = 1) {
const canvas = document.createElement('canvas');
const w = originalImg.width;
const h = originalImg.height;
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
// Draw original image
ctx.drawImage(originalImg, 0, 0);
// 1. Find the brightest object/cluster (potential comet or star) using a scale-down search to optimize performance
const thumbCanvas = document.createElement('canvas');
const scale = Math.min(1, 250 / Math.max(w, h));
const tw = Math.max(3, Math.floor(w * scale));
const th = Math.max(3, Math.floor(h * scale));
thumbCanvas.width = tw;
thumbCanvas.height = th;
const thumbCtx = thumbCanvas.getContext('2d');
thumbCtx.drawImage(originalImg, 0, 0, tw, th);
const thumbData = thumbCtx.getImageData(0, 0, tw, th).data;
let maxBrightness = -1;
let targetX = w / 2;
let targetY = h / 2;
for (let y = 1; y < th - 1; y++) {
for (let x = 1; x < tw - 1; x++) {
let sum = 0;
// 3x3 surrounding pixel averaging to avoid random salt noise
for(let dy = -1; dy <= 1; dy++) {
for(let dx = -1; dx <= 1; dx++) {
let idx = ((y + dy) * tw + (x + dx)) * 4;
let alpha = thumbData[idx + 3] / 255;
// Compute luminous weight considering alpha
let luma = (thumbData[idx] + thumbData[idx+1] + thumbData[idx+2]) * alpha;
sum += luma;
}
}
// Create a slight spatial bias toward the center
let cx = tw / 2;
let cy = th / 2;
let dist = Math.sqrt((x - cx) ** 2 + (y - cy) ** 2);
let centerBias = (1 - dist / (tw + th)) * 10;
if (sum + centerBias > maxBrightness) {
maxBrightness = sum + centerBias;
targetX = x / scale;
targetY = y / scale;
}
}
}
// Ensure numeric input scales
const diag = Math.sqrt(w * w + h * h);
const tailLength = diag * Number(tailLengthScale);
tailAngle = Number(tailAngle);
drawTargetHUD = Number(drawTargetHUD);
// 2. Draw Simulated Comet overlapping the brightest target location
ctx.save();
ctx.translate(targetX, targetY);
ctx.rotate(tailAngle * Math.PI / 180);
// Using screen composition creates a luminous glow over the underlying image
ctx.globalCompositeOperation = 'screen';
// Outer Comet Tail
let tGrad = ctx.createLinearGradient(0, 0, -tailLength, 0);
tGrad.addColorStop(0, tailColor);
tGrad.addColorStop(1, 'rgba(0,0,0,0)');
ctx.fillStyle = tGrad;
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.quadraticCurveTo(-tailLength * 0.3, tailLength * 0.1, -tailLength, tailLength * 0.2);
ctx.lineTo(-tailLength, -tailLength * 0.2);
ctx.quadraticCurveTo(-tailLength * 0.3, -tailLength * 0.1, 0, 0);
ctx.closePath();
ctx.globalAlpha = 0.8;
ctx.fill();
// Inner Brighter Tail
let innerGrad = ctx.createLinearGradient(0, 0, -tailLength * 0.8, 0);
innerGrad.addColorStop(0, '#ffffff');
innerGrad.addColorStop(0.2, tailColor);
innerGrad.addColorStop(1, 'rgba(0,0,0,0)');
ctx.fillStyle = innerGrad;
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.quadraticCurveTo(-tailLength * 0.2, tailLength * 0.05, -tailLength * 0.8, tailLength * 0.05);
ctx.lineTo(-tailLength * 0.8, -tailLength * 0.05);
ctx.quadraticCurveTo(-tailLength * 0.2, -tailLength * 0.05, 0, 0);
ctx.closePath();
ctx.globalAlpha = 0.9;
ctx.fill();
// Nucleus / Coma Glow
let headGrad = ctx.createRadialGradient(0, 0, 0, 0, 0, tailLength * 0.15);
headGrad.addColorStop(0, '#ffffff');
headGrad.addColorStop(0.2, tailColor);
headGrad.addColorStop(1, 'rgba(0,0,0,0)');
ctx.fillStyle = headGrad;
ctx.globalAlpha = 1.0;
ctx.beginPath();
ctx.arc(0, 0, tailLength * 0.15, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
// 3. Draw Astronautical HUD Interface
if (drawTargetHUD) {
ctx.save();
const hudColor = '#00ffcc';
ctx.strokeStyle = hudColor;
ctx.lineWidth = Math.max(1, Math.floor(diag * 0.002));
const fontSize = Math.max(14, Math.floor(diag * 0.015));
ctx.font = `bold ${fontSize}px "Courier New", Courier, monospace`;
ctx.fillStyle = hudColor;
ctx.shadowColor = hudColor;
ctx.shadowBlur = Math.max(2, Math.floor(diag * 0.004));
let s = Math.max(10, Math.floor(diag * 0.015)); // Box corner length marker
let bb = tailLength * 0.15; // Bounding box offset from center
if (bb < 40) bb = 40;
ctx.beginPath();
// Top left framing
ctx.moveTo(targetX - bb, targetY - bb + s);
ctx.lineTo(targetX - bb, targetY - bb);
ctx.lineTo(targetX - bb + s, targetY - bb);
// Top right framing
ctx.moveTo(targetX + bb, targetY - bb + s);
ctx.lineTo(targetX + bb, targetY - bb);
ctx.lineTo(targetX + bb - s, targetY - bb);
// Bottom left framing
ctx.moveTo(targetX - bb, targetY + bb - s);
ctx.lineTo(targetX - bb, targetY + bb);
ctx.lineTo(targetX - bb + s, targetY + bb);
// Bottom right framing
ctx.moveTo(targetX + bb, targetY + bb - s);
ctx.lineTo(targetX + bb, targetY + bb);
ctx.lineTo(targetX + bb - s, targetY + bb);
ctx.stroke();
// Subtle Target Crosshairs
ctx.globalAlpha = 0.5;
ctx.beginPath();
let chExt = bb + s;
ctx.moveTo(targetX - chExt, targetY);
ctx.lineTo(targetX - 5, targetY);
ctx.moveTo(targetX + 5, targetY);
ctx.lineTo(targetX + chExt, targetY);
ctx.moveTo(targetX, targetY - chExt);
ctx.lineTo(targetX, targetY - 5);
ctx.moveTo(targetX, targetY + 5);
ctx.lineTo(targetX, targetY + chExt);
ctx.stroke();
ctx.globalAlpha = 1.0;
// Adaptive HUD Data Placement
let textAlign = 'left';
let textX = targetX + bb + 10;
let textY = targetY - bb;
const textWidthEstimation = fontSize * 10;
if (textX + textWidthEstimation > w) { // Avoid clipping off the right axis
textX = targetX - bb - 10;
textAlign = 'right';
}
if (textY < fontSize * 3) { // Avoid clipping off the top axis
textY = targetY + bb + fontSize;
}
ctx.textAlign = textAlign;
ctx.fillText('TARGET: COMET', textX, textY);
ctx.fillText('STATUS: FOUND', textX, textY + fontSize * 1.2);
// Randomly generated astronomical coordinates for asthetics
const randCoord = () => (Math.random() * 100).toFixed(2);
ctx.fillText(`RA: ${randCoord()} DEC: ${randCoord()}`, textX, textY + fontSize * 2.4);
ctx.restore();
}
return canvas;
}
Apply Changes