You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Creates a black and white, grainy, targeting-pod-like image effect.
* This function converts the input image to a high-contrast black and white image,
* adds noise, and overlays a targeting user interface including a vignette,
* crosshairs, and telemetry text.
*
* @param {Image} originalImg The original javascript Image object.
* @param {number} [grainAmount=30] The intensity of the noise/grain effect. Range 0-255.
* @param {number} [contrast=1.5] The contrast level. 1 is no change.
* @param {number} [vignetteIntensity=0.8] The strength of the vignette effect. Range 0-1.
* @param {string} [crosshairColor='rgba(0, 255, 0, 0.7)'] The color of the UI overlay elements.
* @param {string} [telemetryText='STATUS: AQC\nALT: 25000 FT\nSPD: 120 KTS\nRNG: 12.5 KM\nTGT: 34.0522 N, 118.2437 W\nCAM: DTV'] The text to display, with \n for new lines.
* @returns {HTMLCanvasElement} A new canvas element with the processed image.
*/
function processImage(originalImg, grainAmount = 30, contrast = 1.5, vignetteIntensity = 0.8, crosshairColor = 'rgba(0, 255, 0, 0.7)', telemetryText = 'STATUS: AQC\nALT: 25000 FT\nSPD: 120 KTS\nRNG: 12.5 KM\nTGT: 34.0522 N, 118.2437 W\nCAM: DTV') {
// 1. Canvas Setup
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const width = originalImg.naturalWidth;
const height = originalImg.naturalHeight;
canvas.width = width;
canvas.height = height;
// 2. Image Processing: B&W, Contrast, Grain
ctx.drawImage(originalImg, 0, 0, width, height);
const imageData = ctx.getImageData(0, 0, width, height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
// Grayscale conversion (luminosity method)
const gray = 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2];
// Contrast adjustment
let contrastedGray = ((gray / 255.0) - 0.5) * contrast + 0.5;
contrastedGray *= 255;
// Add grain/noise
const noise = (Math.random() - 0.5) * grainAmount;
let finalValue = contrastedGray + noise;
// Clamp the value between 0 and 255
finalValue = Math.max(0, Math.min(255, finalValue));
data[i] = data[i + 1] = data[i + 2] = finalValue;
}
ctx.putImageData(imageData, 0, 0);
// 3. Overlay Elements
const centerX = width / 2;
const centerY = height / 2;
// Vignette
if (vignetteIntensity > 0) {
const outerRadius = Math.sqrt(centerX * centerX + centerY * centerY);
const gradient = ctx.createRadialGradient(centerX, centerY, outerRadius * (1 - vignetteIntensity), centerX, centerY, outerRadius);
gradient.addColorStop(0, 'rgba(0,0,0,0)');
gradient.addColorStop(1, 'rgba(0,0,0,0.95)');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, height);
}
ctx.strokeStyle = crosshairColor;
ctx.fillStyle = crosshairColor;
ctx.lineWidth = 1;
// Crosshairs
const crosshairArmLength = 50;
const centralBoxSize = 8;
const tickSize = 10;
// Central Box
ctx.strokeRect(centerX - centralBoxSize / 2, centerY - centralBoxSize / 2, centralBoxSize, centralBoxSize);
// Arms of the crosshair
ctx.beginPath();
ctx.moveTo(centerX + centralBoxSize / 2, centerY);
ctx.lineTo(centerX + centralBoxSize / 2 + crosshairArmLength, centerY);
ctx.moveTo(centerX - centralBoxSize / 2, centerY);
ctx.lineTo(centerX - centralBoxSize / 2 - crosshairArmLength, centerY);
ctx.moveTo(centerX, centerY + centralBoxSize / 2);
ctx.lineTo(centerX, centerY + centralBoxSize / 2 + crosshairArmLength);
ctx.moveTo(centerX, centerY - centralBoxSize / 2);
ctx.lineTo(centerX, centerY - centralBoxSize / 2 - crosshairArmLength);
ctx.stroke();
// Ticks on crosshair arms
ctx.beginPath();
for (let i = 1; i <= 4; i++) {
const pos = centralBoxSize / 2 + i * 10;
// Right arm
ctx.moveTo(centerX + pos, centerY - tickSize / 2);
ctx.lineTo(centerX + pos, centerY + tickSize / 2);
// Left arm
ctx.moveTo(centerX - pos, centerY - tickSize / 2);
ctx.lineTo(centerX - pos, centerY + tickSize / 2);
// Bottom arm
ctx.moveTo(centerX - tickSize / 2, centerY + pos);
ctx.lineTo(centerX + tickSize / 2, centerY + pos);
// Top arm
ctx.moveTo(centerX - tickSize / 2, centerY - pos);
ctx.lineTo(centerX + tickSize / 2, centerY - pos);
}
ctx.stroke();
// Telemetry Text
const padding = 15;
const fontSize = Math.max(12, Math.min(width, height) / 35);
const lineHeight = fontSize * 1.25;
ctx.font = `${fontSize}px "Courier New", monospace`;
// Top-left text
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
const textLines = telemetryText.split('\n');
let yPos = padding;
textLines.forEach(line => {
ctx.fillText(line, padding, yPos);
yPos += lineHeight;
});
// Bottom-right text (randomized for effect)
ctx.textAlign = 'right';
ctx.textBaseline = 'bottom';
ctx.fillText(`MGRS: ${Math.floor(Math.random()*90000) + 10000}`, width - padding, height - padding - (lineHeight * 2));
ctx.fillText(`LA: ${Math.floor(Math.random()*90000) + 10000}`, width - padding, height - padding - lineHeight);
ctx.fillText(`CAGE: ${Math.floor(Math.random()*90000) + 10000}`, width - padding, height - padding);
// 4. Return the canvas
return canvas;
}
Apply Changes