You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, noiseIntensity = 25, blurRadius = 1.5, camText = "CAM-01", isBlackAndWhite = 1) {
// Parameter casting
const noiseAlpha = Math.max(0, Math.min(255, Number(noiseIntensity)));
const blur = Math.max(0, Number(blurRadius));
const bwMode = Number(isBlackAndWhite);
// Create main canvas
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Set dimensions based on the original image
const width = originalImg.width || originalImg.naturalWidth || 800;
const height = originalImg.height || originalImg.naturalHeight || 600;
canvas.width = width;
canvas.height = height;
// Apply blur and optionally black & white (grayscale) filters
let filters = [];
if (blur > 0) {
filters.push(`blur(${blur}px)`);
}
if (bwMode === 1) {
filters.push('grayscale(100%)', 'contrast(120%)');
}
ctx.filter = filters.length > 0 ? filters.join(' ') : 'none';
// Draw the original image
ctx.drawImage(originalImg, 0, 0, width, height);
// Reset filters so overlays remain sharp
ctx.filter = 'none';
// Generate and apply sensor noise (Pattern-based to bypass potential CORS limitations & increase performance)
if (noiseAlpha > 0) {
const noiseCanvas = document.createElement('canvas');
noiseCanvas.width = 128; // Small tile size for performance
noiseCanvas.height = 128;
const nCtx = noiseCanvas.getContext('2d');
const nImgData = nCtx.createImageData(128, 128);
const data = nImgData.data;
for (let i = 0; i < data.length; i += 4) {
const val = Math.random() < 0.5 ? 0 : 255;
data[i] = val; // R
data[i + 1] = val; // G
data[i + 2] = val; // B
data[i + 3] = (Math.random() * noiseAlpha); // Randomize alpha slightly for natural look
}
nCtx.putImageData(nImgData, 0, 0);
ctx.globalCompositeOperation = 'overlay';
ctx.fillStyle = ctx.createPattern(noiseCanvas, 'repeat');
ctx.fillRect(0, 0, width, height);
ctx.globalCompositeOperation = 'source-over';
}
// Apply scanlines
ctx.fillStyle = 'rgba(0, 0, 0, 0.15)';
for (let y = 0; y < height; y += 4) {
ctx.fillRect(0, y, width, 1);
}
// Calculate UI scale based on image dimensions
const minDim = Math.min(width, height);
const margin = Math.max(20, minDim * 0.05);
const bracketLen = Math.max(30, minDim * 0.1);
const bracketThick = Math.max(2, minDim * 0.006);
// Overlay settings
ctx.strokeStyle = 'rgba(255, 255, 255, 0.85)';
ctx.lineWidth = bracketThick;
ctx.lineCap = 'square';
ctx.lineJoin = 'miter';
// 1. Draw 4-corner bracket frame (Viewfinder)
ctx.beginPath();
// Top-Left
ctx.moveTo(margin + bracketLen, margin);
ctx.lineTo(margin, margin);
ctx.lineTo(margin, margin + bracketLen);
// Top-Right
ctx.moveTo(width - margin - bracketLen, margin);
ctx.lineTo(width - margin, margin);
ctx.lineTo(width - margin, margin + bracketLen);
// Bottom-Right
ctx.moveTo(width - margin - bracketLen, height - margin);
ctx.lineTo(width - margin, height - margin);
ctx.lineTo(width - margin, height - margin - bracketLen);
// Bottom-Left
ctx.moveTo(margin + bracketLen, height - margin);
ctx.lineTo(margin, height - margin);
ctx.lineTo(margin, height - margin - bracketLen);
ctx.stroke();
// 2. Draw Center Crosshair (+)
const cx = width / 2;
const cy = height / 2;
const cLen = bracketLen * 0.4;
ctx.beginPath();
ctx.moveTo(cx - cLen, cy);
ctx.lineTo(cx + cLen, cy);
ctx.moveTo(cx, cy - cLen);
ctx.lineTo(cx, cy + cLen);
ctx.stroke();
// Text Styling Setup
const fontSize = Math.max(12, minDim * 0.035);
ctx.font = `bold ${fontSize}px "Courier New", Courier, monospace`;
ctx.textBaseline = 'top';
// Helper to draw readable text against any background
const drawCText = (text, x, y, align) => {
ctx.textAlign = align;
// Stroke/Shadow for readability
ctx.lineWidth = Math.max(2, fontSize * 0.15);
ctx.strokeStyle = 'rgba(0, 0, 0, 0.7)';
ctx.strokeText(text, x, y);
// Fill
ctx.fillStyle = 'rgba(255, 255, 255, 0.9)';
ctx.fillText(text, x, y);
};
// 3. Draw Timestamp / Camera ID (Top-Left)
const now = new Date();
const pad = (n) => n.toString().padStart(2, '0');
const dateStr = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
drawCText(camText, margin + bracketThick + 5, margin + bracketThick + 5, 'left');
drawCText(dateStr, margin + bracketThick + 5, margin + bracketThick + 5 + fontSize * 1.3, 'left');
// 4. Draw Recording Indicator (Top-Right)
const recText = "REC";
const xPos = width - margin - bracketThick - 5;
const yPos = margin + bracketThick + 5;
drawCText(recText, xPos, yPos, 'right');
// Red Dot
const textMetrics = ctx.measureText(recText);
const dotRadius = fontSize * 0.35;
const dotX = xPos - textMetrics.width - dotRadius - 10;
const dotY = yPos + fontSize / 2.2;
ctx.beginPath();
ctx.arc(dotX, dotY, dotRadius, 0, Math.PI * 2);
ctx.fillStyle = '#ff0000';
ctx.fill();
// Outer red glow for blinking simulation
ctx.beginPath();
ctx.arc(dotX, dotY, dotRadius * 2, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255, 0, 0, 0.3)';
ctx.fill();
return canvas;
}
Apply Changes