You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, topText = 'ТЕЛЕПРОГРАММА', bottomText = 'СТУДИЯ', fontColor = '#FFFFFF', fontFamily = 'VT323', fontSize = 40, scanlineOpacity = 0.1, noiseAmount = 0.05, chromaticAberrationAmount = 2) {
// 1. Parameter type coercion
const numFontSize = Number(fontSize);
const numScanlineOpacity = Number(scanlineOpacity);
const numNoiseAmount = Number(noiseAmount);
const numChromaticAberrationAmount = Number(chromaticAberrationAmount);
// 2. Canvas Setup
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', {
willReadFrequently: true
});
canvas.width = originalImg.naturalWidth;
canvas.height = originalImg.naturalHeight;
// 3. Dynamic Font Loading
try {
const font = `${numFontSize}px "${fontFamily}"`;
const fontUrl = `https://fonts.googleapis.com/css2?family=${fontFamily.replace(/ /g, '+')}&display=swap`;
let fontAvailable = false;
if (document.fonts) {
fontAvailable = await document.fonts.check(font);
}
if (!fontAvailable) {
if (!document.querySelector(`link[href="${fontUrl}"]`)) {
const link = document.createElement('link');
link.href = fontUrl;
link.rel = 'stylesheet';
document.head.appendChild(link);
// Wait for the font to be loaded
await new Promise(resolve => {
link.onload = resolve;
link.onerror = resolve; // Resolve even on error to not block the script
});
}
if (document.fonts) {
await document.fonts.load(font);
}
}
} catch (e) {
console.error("Font loading failed, continuing with default font.", e);
}
// 4. Draw the original image onto the canvas
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
// 5. Apply pixel-level effects (Chromatic Aberration and Noise)
if (numChromaticAberrationAmount > 0 || numNoiseAmount > 0) {
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
const width = canvas.width;
const originalData = new Uint8ClampedArray(data); // Copy for safe reading
for (let i = 0; i < data.length; i += 4) {
// --- Chromatic Aberration ---
if (numChromaticAberrationAmount > 0) {
const shift = Math.floor(numChromaticAberrationAmount);
const x = (i / 4) % width;
// Read Red channel from x - shift
const rIndex = i - (shift * 4);
if (x >= shift) {
data[i] = originalData[rIndex]; // R
}
// Read Blue channel from x + shift
const bIndex = i + (shift * 4);
if (x < width - shift) {
data[i + 2] = originalData[bIndex + 2]; // B
}
}
// --- Noise ---
if (numNoiseAmount > 0) {
const noise = (Math.random() - 0.5) * 255 * numNoiseAmount;
data[i] = Math.max(0, Math.min(255, data[i] + noise));
data[i + 1] = Math.max(0, Math.min(255, data[i + 1] + noise));
data[i + 2] = Math.max(0, Math.min(255, data[i + 2] + noise));
}
}
ctx.putImageData(imageData, 0, 0);
}
// 6. Apply Scanline Overlay
if (numScanlineOpacity > 0) {
ctx.fillStyle = `rgba(0, 0, 0, ${numScanlineOpacity})`;
for (let y = 0; y < canvas.height; y += 3) {
ctx.fillRect(0, y, canvas.width, 1);
}
}
// 7. Draw Text and UI Elements
ctx.font = `${numFontSize}px "${fontFamily}"`;
ctx.fillStyle = fontColor;
ctx.shadowColor = 'rgba(0,0,0,0.7)';
ctx.shadowBlur = 5;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
// Top and Bottom Texts
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
ctx.fillText(topText.toUpperCase(), canvas.width / 2, numFontSize * 0.5);
ctx.textBaseline = 'bottom';
ctx.fillText(bottomText.toUpperCase(), canvas.width / 2, canvas.height - numFontSize * 0.5);
// REC indicator
const recFontSize = Math.max(12, Math.floor(numFontSize / 2));
const padding = recFontSize;
ctx.font = `${recFontSize}px "${fontFamily}"`;
ctx.textAlign = 'left';
ctx.textBaseline = 'middle';
// Red dot
ctx.fillStyle = '#FF0000';
ctx.beginPath();
ctx.arc(padding, padding, recFontSize / 2, 0, 2 * Math.PI);
ctx.fill();
// "REC" text
ctx.fillStyle = fontColor;
ctx.fillText('REC', padding + recFontSize, padding);
// Clear shadow for subsequent draws if any
ctx.shadowColor = 'transparent';
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
// 8. Return the final canvas
return canvas;
}
Apply Changes