You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, noiseAmount = 25, pixelationFactor = 0.5, jpegQuality = 0.3, saturation = 0.8, addTimestamp = "true") {
/**
* Applies a 2000s low-quality digital camera effect to an image.
*
* @param {Image} originalImg - The source Image object.
* @param {number} noiseAmount - The intensity of the digital noise/grain. Range: 0 upwards. Default: 25.
* @param {number} pixelationFactor - The factor to downscale and then upscale the image to create a pixelated effect. Range: 0.1 to 1.0. Smaller values mean more pixelation. Default: 0.5.
* @param {number} jpegQuality - The quality level for JPEG compression to introduce artifacts. Range: 0.0 to 1.0. Lower values mean more artifacts. Default: 0.3.
* @param {number} saturation - Adjusts the color saturation. 1.0 is original, 0.0 is grayscale. Lower values feel more 'vintage'. Default: 0.8.
* @param {string} addTimestamp - Whether to add a classic orange timestamp. Accepts "true" or "false". Default: "true".
* @returns {Promise<HTMLCanvasElement>} A canvas element with the filter applied.
*/
// --- 1. Setup Canvas ---
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const width = originalImg.width;
const height = originalImg.height;
canvas.width = width;
canvas.height = height;
// --- 2. Pixelation Effect ---
// This simulates the low resolution of early digital cameras.
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');
// Clamp pixelationFactor for stability
const pFactor = Math.max(0.1, Math.min(1, Number(pixelationFactor)));
const smallWidth = width * pFactor;
const smallHeight = height * pFactor;
tempCanvas.width = smallWidth;
tempCanvas.height = smallHeight;
// Draw original image onto the small canvas
tempCtx.drawImage(originalImg, 0, 0, smallWidth, smallHeight);
// Draw the small canvas back onto the main canvas, scaled up, with no smoothing
ctx.imageSmoothingEnabled = false; // Critical for the pixelated look
ctx.drawImage(tempCanvas, 0, 0, smallWidth, smallHeight, 0, 0, width, height);
// --- 3. JPEG Compression Artifacts ---
// This simulates the aggressive compression used to save space on small memory cards.
await new Promise(resolve => {
const quality = Math.max(0.05, Math.min(1.0, Number(jpegQuality)));
const dataUrl = canvas.toDataURL('image/jpeg', quality);
const jpegImg = new Image();
jpegImg.onload = () => {
ctx.drawImage(jpegImg, 0, 0, width, height);
resolve();
};
jpegImg.onerror = () => { // Fallback if image loading fails
resolve();
};
jpegImg.src = dataUrl;
});
// --- 4. Pixel-level Manipulation (Color Shift, Saturation, Noise) ---
const imageData = ctx.getImageData(0, 0, width, height);
const data = imageData.data;
const noise = Math.max(0, Number(noiseAmount));
const satValue = Math.max(0, Math.min(2, Number(saturation)));
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
// a) Desaturation: Mimics less vibrant sensors.
const gray = 0.299 * r + 0.587 * g + 0.114 * b; // Luminance calculation
r = gray + satValue * (r - gray);
g = gray + satValue * (g - gray);
b = gray + satValue * (b - gray);
// b) Warm Tint: Mimics the color science of older CCD sensors.
r += 10;
g += 5;
// c) Noise: Simulates sensor noise, especially in low light.
const noiseVal = (Math.random() - 0.5) * noise;
r += noiseVal;
g += noiseVal;
b += noiseVal;
// Clamp values to the valid 0-255 range
data[i] = Math.max(0, Math.min(255, r));
data[i + 1] = Math.max(0, Math.min(255, g));
data[i + 2] = Math.max(0, Math.min(255, b));
}
ctx.putImageData(imageData, 0, 0);
// --- 5. Add Timestamp ---
const shouldAddTimestamp = addTimestamp.toString().toLowerCase() === 'true';
if (shouldAddTimestamp) {
// Generate a plausible date string from the 2000s for authenticity
const year = 2002 + Math.floor(Math.random() * 6); // Random year 2002-2007
const month = (Math.floor(Math.random() * 12) + 1).toString().padStart(2, '0');
const day = (Math.floor(Math.random() * 28) + 1).toString().padStart(2, '0');
const dateString = `${month} ${day} '${year.toString().substring(2)}`;
// Calculate font size relative to image width for scalability
const fontSize = Math.max(12, Math.round(width / 45));
ctx.font = `bold ${fontSize}px 'Courier New', monospace`;
ctx.fillStyle = '#FFA500'; // Classic orange-yellow
// Add a subtle shadow/stroke for readability, common in camera timestamps
ctx.shadowColor = 'rgba(0, 0, 0, 0.8)';
ctx.shadowBlur = Math.round(fontSize / 10);
ctx.shadowOffsetX = 1;
ctx.shadowOffsetY = 1;
// Position text at the bottom right corner
const textMetrics = ctx.measureText(dateString);
const textWidth = textMetrics.width;
const padding = Math.round(fontSize / 2);
const x = width - textWidth - padding;
const y = height - padding;
ctx.fillText(dateString, x, y);
}
return canvas;
}
Apply Changes