You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, overlayOpacity = "0.75", fontSize = "12", textColor = "#FFFFFF") {
// Basic canvas setup
const canvas = document.createElement('canvas');
const w = originalImg.naturalWidth || originalImg.width || 800;
const h = originalImg.naturalHeight || originalImg.height || 600;
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
// Draw the underlying image
ctx.drawImage(originalImg, 0, 0, w, h);
// Attempt to extract EXIF data over dynamic import if provided
let exifData = null;
try {
const exifrModule = await import('https://cdn.jsdelivr.net/npm/exifr/dist/lite.esm.js');
exifData = await exifrModule.default.parse(originalImg);
} catch (e) {
console.warn("EXIF parsing failed or not present", e);
}
// Helper functions for fake video ID hashes
const generateID = (len) => {
let text = "";
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
for (let i = 0; i < len; i++) text += charset.charAt(Math.floor(Math.random() * charset.length));
return text;
};
const generateAlpha = (len) => {
let text = "";
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
for (let i = 0; i < len; i++) text += charset.charAt(Math.floor(Math.random() * charset.length));
return text;
};
const scpn = `${generateAlpha(4)} ${generateAlpha(4)} ${generateAlpha(4)}`;
// Initial standard lines mimicking YouTube Stats For Nerds
const lines = [
{ k: "Video ID / sCPN", v: `${generateID(11)} / ${scpn}` },
{ k: "Viewport / Frames", v: `${w}x${h} / 0 dropped` },
{ k: "Current / Optimal Res", v: `${w}x${h} / ${w}x${h}` },
{ k: "Volume / Normalized", v: "100% / 100%" }
];
let insertIdx = 4;
// Inject Exif Properties to look like genuine metadata
if (exifData) {
const camera = [exifData.Make, exifData.Model].filter(Boolean).join(" ");
if (camera) lines.splice(insertIdx++, 0, { k: "Camera / Make", v: camera.substring(0, 30) });
let settings = [];
if (exifData.ExposureTime) settings.push(`1/${Math.round(1 / exifData.ExposureTime)}s`);
if (exifData.FNumber) settings.push(`f/${exifData.FNumber}`);
if (exifData.ISO) settings.push(`ISO ${exifData.ISO}`);
if (settings.length > 0) lines.splice(insertIdx++, 0, { k: "Exposure / ISO", v: settings.join(", ") });
if (exifData.Software) lines.splice(insertIdx++, 0, { k: "Software", v: String(exifData.Software).substring(0, 30) });
if (exifData.DateTimeOriginal) {
const d = new Date(exifData.DateTimeOriginal);
if (!isNaN(d.getTime())) lines.splice(insertIdx++, 0, { k: "Original Date", v: d.toISOString().split('T')[0] });
}
}
// Typical YT Network activity padding
lines.push(
{ k: "Codecs", v: "image/auto" },
{ k: "Color", v: "sRGB / bt709" },
{ k: "Connection Speed", v: `${Math.floor(Math.random() * 50000 + 10000)} Kbps` },
{ k: "Network Activity", v: "0 KB" },
{ k: "Buffer Health", v: "0.00 s" },
{ k: "Mystery Text", v: `s:${generateAlpha(2)} t:${generateAlpha(4)} m:${generateAlpha(2)}` }
);
// Precalculate Box Dimensions based on Text
const parsedFontSize = parseInt(fontSize) || 12;
const fontStr = `${parsedFontSize}px "Courier New", Courier, monospace`;
ctx.font = fontStr;
let maxK = 0;
let maxV = 0;
lines.forEach(l => {
const wK = ctx.measureText(l.k).width;
const wV = ctx.measureText(l.v).width;
if (wK > maxK) maxK = wK;
if (wV > maxV) maxV = wV;
});
const padding = 16;
const gap = 24;
const lineHeight = parsedFontSize + 6;
const boxWidth = padding * 2 + maxK + gap + maxV + 15; // 15 for Close Button spacing
const boxHeight = padding * 2 + lines.length * lineHeight;
const boxX = Math.min(15, w * 0.02);
const boxY = Math.min(15, h * 0.02);
ctx.save();
// Scale down overlay layout if the image is too small to fit the panel
let scaleFit = 1;
if (w < boxWidth + boxX * 2 || h < boxHeight + boxY * 2) {
scaleFit = Math.min((w - boxX * 2) / boxWidth, (h - boxY * 2) / boxHeight);
if (scaleFit < 0.1) scaleFit = 0.1;
ctx.translate(boxX, boxY);
ctx.scale(scaleFit, scaleFit);
ctx.translate(-boxX, -boxY);
}
// Draw Stats For Nerds Background window
ctx.fillStyle = `rgba(0, 0, 0, ${parseFloat(overlayOpacity) || 0.75})`;
if (ctx.roundRect) {
ctx.beginPath();
ctx.roundRect(boxX, boxY, boxWidth, boxHeight, 5);
ctx.fill();
} else {
ctx.fillRect(boxX, boxY, boxWidth, boxHeight);
}
// Draw close button '✕' on top right
ctx.fillStyle = textColor;
ctx.font = `bold ${parsedFontSize + 2}px "Helvetica Neue", Helvetica, Arial, sans-serif`;
ctx.globalAlpha = 1.0;
const xPos = boxX + boxWidth - padding;
const yPos = boxY + padding + (parsedFontSize / 2);
ctx.fillText("✕", xPos - ctx.measureText("✕").width / 2, yPos + 2);
// Draw Metrics Data
ctx.font = fontStr;
lines.forEach((l, index) => {
const y = boxY + padding + (index * lineHeight) + parsedFontSize;
// Key Title
ctx.globalAlpha = 0.65;
ctx.fillText(l.k, boxX + padding, y);
// Key Value
ctx.globalAlpha = 0.95;
ctx.fillText(l.v, boxX + padding + maxK + gap, y);
});
ctx.restore();
return canvas;
}
Apply Changes