You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(
originalImg,
volumeInfo = "100% / 329% (-19.0dB/-14.0dB) Opus (-26.0dB/-14.0dB) Ac3",
overlayOpacity = 0.7,
fontSize = 13
) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Create a mock set of typical YouTube Stats for Nerds to make it realistic
// and seamlessly incorporate the requested Volume/Normalization information.
const statsData = [
{ label: "Video ID / sCPN", value: "xQ_IegoXYZ / 7V9B-8J2S-K1M4" },
{ label: "Viewport / Frames", value: "1920x1080 / 0 dropped of 1240" },
{ label: "Current / Optimal Res", value: "1920x1080@60 / 1920x1080@60" },
{ label: "Volume / Normalized", value: volumeInfo },
{ label: "Codecs", value: "vp09.00.51.08.01.01.01.01.00 (303) / opus (251)" },
{ label: "Color", value: "bt709 / bt709" },
{ label: "Host", value: "r1---sn-aaaaaa.googlevideo.com" },
{ label: "Connection Speed", value: "84000 Kbps" },
{ label: "Network Activity", value: "0 KB" },
{ label: "Buffer Health", value: "45.2 s" },
{ label: "Mystery Text", value: "s:4 t:173.45 b:429.231 P" }
];
// Configure the font context for measuring text dimensions
ctx.font = `${fontSize}px "Roboto", "Helvetica Neue", "Arial", sans-serif`;
// Calculate layout metrics
let maxLabelWidth = 0;
let maxValueWidth = 0;
statsData.forEach(stat => {
const labelWidth = ctx.measureText(stat.label).width;
const valueWidth = ctx.measureText(stat.value).width;
if (labelWidth > maxLabelWidth) maxLabelWidth = labelWidth;
if (valueWidth > maxValueWidth) maxValueWidth = valueWidth;
});
const padding = 16;
const gap = 20; // Space between label column and value column
const lineHeight = fontSize * 1.6;
const overlayWidth = padding * 2 + maxLabelWidth + gap + maxValueWidth + 24; // extra right padding for aesthetics
const overlayHeight = padding * 2 + statsData.length * lineHeight;
// Ensure the canvas is at least big enough to fit the overlay, even for tiny images
canvas.width = Math.max(originalImg.width, overlayWidth + 40);
canvas.height = Math.max(originalImg.height, overlayHeight + 40);
// If canvas was enlarged, fill background with a neutral black
if (canvas.width > originalImg.width || canvas.height > originalImg.height) {
ctx.fillStyle = '#000000';
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
// Draw the original image as the background
ctx.drawImage(originalImg, 0, 0);
// Position the overlay at the top left, similar to actual YouTube player behavior
const startX = 20;
const startY = 20;
// Draw the translucent dark overlay bounding box
ctx.fillStyle = `rgba(28, 28, 28, ${overlayOpacity})`;
if (ctx.roundRect) {
ctx.beginPath();
ctx.roundRect(startX, startY, overlayWidth, overlayHeight, 4);
ctx.fill();
} else {
// Fallback for older browsers
ctx.fillRect(startX, startY, overlayWidth, overlayHeight);
}
// Draw the close button (✕) in the top-right corner over the overlay box
ctx.fillStyle = '#ffffff';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.font = `bold ${fontSize + 2}px sans-serif`;
ctx.fillText("✕", startX + overlayWidth - 16, startY + 16);
// Draw the text lines correctly aligned
ctx.textAlign = 'left';
ctx.font = `400 ${fontSize}px "Roboto", "Helvetica Neue", "Arial", sans-serif`;
statsData.forEach((stat, index) => {
const y = startY + padding + (index * lineHeight) + (lineHeight / 2); // Center baseline
const labelX = startX + padding;
const valueX = startX + padding + maxLabelWidth + gap;
// Label Color (Light Gray)
ctx.fillStyle = '#b3b3b3';
ctx.fillText(stat.label, labelX, y);
// Value Color (Crisp White)
ctx.fillStyle = '#ffffff';
ctx.fillText(stat.value, valueX, y);
});
return canvas;
}
Apply Changes