You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, contrast = 150, brightness = -10, noiseAmount = 0.05, scanlineIntensity = 0.1, vignetteIntensity = 0.7, timestampText = 'REC ● 04:28:1983') {
/**
* Creates an "analog horror" style image effect reminiscent of the "Telepath Case" meme.
* @param {Image} originalImg - The input Image object.
* @param {number} contrast - The contrast level (100 is normal).
* @param {number} brightness - The brightness level (0 is normal).
* @param {number} noiseAmount - The amount of random noise (0 to 1).
* @param {number} scanlineIntensity - The darkness of the horizontal scanlines (0 to 1).
* @param {number} vignetteIntensity - The darkness of the vignette effect at the edges (0 to 1).
* @param {string} timestampText - The text to display in the bottom-left corner.
* @returns {Promise<HTMLCanvasElement>} A canvas element with the processed image.
*/
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const w = originalImg.naturalWidth;
const h = originalImg.naturalHeight;
canvas.width = w;
canvas.height = h;
// 1. Apply base filters (grayscale, contrast, brightness)
// The filter is applied when drawing the initial image.
const contrastValue = contrast / 100;
const brightnessValue = (100 + brightness) / 100;
ctx.filter = `grayscale(1) contrast(${contrastValue}) brightness(${brightnessValue})`;
ctx.drawImage(originalImg, 0, 0, w, h);
ctx.filter = 'none'; // Reset filter for subsequent drawing operations
// 2. Apply noise by manipulating pixel data
if (noiseAmount > 0) {
const imageData = ctx.getImageData(0, 0, w, h);
const data = imageData.data;
const noiseStrength = noiseAmount * 255;
for (let i = 0; i < data.length; i += 4) {
// Generate random noise and add it to each color channel
const noise = (Math.random() - 0.5) * noiseStrength;
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);
}
// 3. Apply CRT Scanlines
if (scanlineIntensity > 0) {
ctx.fillStyle = `rgba(0, 0, 0, ${scanlineIntensity})`;
for (let i = 0; i < h; i += 4) { // Draw a line every 4 pixels
ctx.fillRect(0, i, w, 2); // Each line is 2 pixels thick
}
}
// 4. Apply vignette effect
if (vignetteIntensity > 0) {
const centerX = w / 2;
const centerY = h / 2;
const outerRadius = Math.sqrt(w * w + h * h) / 2;
const gradient = ctx.createRadialGradient(centerX, centerY, outerRadius * 0.3, centerX, centerY, outerRadius);
gradient.addColorStop(0, 'rgba(0,0,0,0)');
gradient.addColorStop(1, `rgba(0,0,0,${vignetteIntensity})`);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, w, h);
}
// 5. Draw Timestamp
if (timestampText && String(timestampText).trim() !== '') {
const fontName = 'VT323';
let effectiveFontName = 'monospace'; // Fallback font
try {
// Check if font is already available to avoid re-downloading
if (!document.fonts.check(`1em ${fontName}`)) {
const fontUrl = `https://fonts.gstatic.com/s/vt323/v17/loBsmMgmgl3_a3a_f9a_1_I.woff2`;
const fontFace = new FontFace(fontName, `url(${fontUrl})`);
const loadedFont = await fontFace.load();
document.fonts.add(loadedFont);
}
effectiveFontName = fontName;
} catch (e) {
console.error('Custom font could not be loaded, falling back to monospace:', e);
}
const fontSize = Math.max(12, Math.round(h / 25));
ctx.font = `${fontSize}px ${effectiveFontName}`;
ctx.fillStyle = 'rgba(255, 255, 200, 0.75)'; // A yellowish古いscreen color
ctx.textAlign = 'left';
ctx.textBaseline = 'bottom';
// Add a slight shadow/glow to mimic CRT bloom
ctx.shadowColor = 'rgba(255, 255, 200, 0.5)';
ctx.shadowBlur = Math.max(2, fontSize / 8);
const margin = fontSize * 0.75;
ctx.fillText(String(timestampText), margin, h - margin);
// Reset shadow for a clean state
ctx.shadowColor = 'transparent';
ctx.shadowBlur = 0;
}
return canvas;
}
Apply Changes