Image Ricoh GR Film Camera Filter Effect Application
(Free & Supports Bulk Upload)
The result will appear here...
JavaScript Code (For Advanced Users)
You can edit the below JavaScript code to customize the image tool.
function processImage(
originalImg,
desaturation = 1.0, // 0.0 (original color) to 1.0 (full grayscale)
contrast = 1.5, // Factor for contrast adjustment (e.g., 1.0 is no change, 1.5 is 50% more contrast)
grain = 25, // Intensity of film grain (0 for no grain, typically 10-50 for visible grain)
vignetteAmount = 0.3, // Strength of vignette effect (0.0 to 1.0)
vignetteFalloff = 2.0, // Controls how sharply the vignette transitions (e.g., 1.0 for linear, 2.0 for quadratic)
tintColor = "none", // Hex color string (e.g., "#0000FF" for blue) or "none" for no tint
tintStrength = 0.1 // Opacity of the tint (0.0 to 1.0)
) {
// Helper function to parse hex color string (e.g., "#RRGGBB" or "#RGB")
function hexToRgb(hex) {
if (!hex || typeof hex !== 'string' || hex.toLowerCase() === "none") {
return null;
}
// Expand shorthand form (e.g. "#03F") to full form (e.g. "#0033FF")
let shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function(m, r, g, b) {
return r + r + g + g + b + b;
});
let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
const canvas = document.createElement('canvas');
// Using { willReadFrequently: true } can optimize frequent getImageData/putImageData calls
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const imgWidth = originalImg.naturalWidth || originalImg.width;
const imgHeight = originalImg.naturalHeight || originalImg.height;
canvas.width = imgWidth;
canvas.height = imgHeight;
// Draw the original image onto the canvas
ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
let imageData;
try {
imageData = ctx.getImageData(0, 0, imgWidth, imgHeight);
} catch (e) {
// This can happen due to cross-origin restrictions if the image isn't hosted on the same domain
// or doesn't have appropriate CORS headers.
console.error("Error getting image data for processing: ", e);
// As a fallback, return a canvas with an error message, or re-throw, or return original untouched (but on a new canvas)
// For this implementation, we'll return a canvas with an error message drawn on it.
ctx.clearRect(0,0,canvas.width, canvas.height); // Clear previously drawn image
ctx.fillStyle = "rgba(0,0,0,0.7)";
ctx.fillRect(0,0,canvas.width, canvas.height);
ctx.fillStyle = "white";
ctx.font = "16px Arial";
ctx.textAlign = "center";
ctx.fillText("Error: Could not process image due to security restrictions.", canvas.width / 2, canvas.height / 2 - 10);
ctx.fillText("Ensure image is from the same domain or has CORS approval.", canvas.width / 2, canvas.height / 2 + 10);
return canvas;
}
const data = imageData.data;
const parsedTint = hexToRgb(tintColor);
const centerX = imgWidth / 2;
const centerY = imgHeight / 2;
// Maximum distance from the center to a corner, used for normalizing vignette effect
const maxDist = Math.sqrt(centerX * centerX + centerY * centerY);
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i+1];
let b = data[i+2];
// 1. Desaturation
if (desaturation > 0) {
// Standard luminance calculation
const lum = 0.299 * r + 0.587 * g + 0.114 * b;
// Blend original color with luminance based on desaturation amount
r = r * (1 - desaturation) + lum * desaturation;
g = g * (1 - desaturation) + lum * desaturation;
b = b * (1 - desaturation) + lum * desaturation;
}
// 2. Tint
if (parsedTint && tintStrength > 0) {
r = r * (1 - tintStrength) + parsedTint.r * tintStrength;
g = g * (1 - tintStrength) + parsedTint.g * tintStrength;
b = b * (1 - tintStrength) + parsedTint.b * tintStrength;
}
// 3. Contrast
if (contrast !== 1.0) { // contrast = 1.0 means no change
// Adjust contrast: normalize to [0,1], shift midpoint to 0, scale, shift back, then scale to [0,255]
r = ((r / 255 - 0.5) * contrast + 0.5) * 255;
g = ((g / 255 - 0.5) * contrast + 0.5) * 255;
b = ((b / 255 - 0.5) * contrast + 0.5) * 255;
}
// 4. Grain
if (grain > 0) {
// Add random noise to each channel. (Math.random() - 0.5) gives range [-0.5, 0.5]
const noise = (Math.random() - 0.5) * grain;
r += noise;
g += noise;
b += noise;
}
// 5. Vignette
if (vignetteAmount > 0 && maxDist > 0) { // maxDist > 0 check to prevent division by zero for 0x0 images
const currentX = (i / 4) % imgWidth;
const currentY = Math.floor((i / 4) / imgWidth);
const dx = centerX - currentX;
const dy = centerY - currentY;
const dist = Math.sqrt(dx * dx + dy * dy);
// Normalized distance from center (0 at center, 1 at maxDist)
const normDist = dist / maxDist;
// Calculate vignette effect strength at this pixel
// Math.pow(normDist, vignetteFalloff) determines how quickly the vignette effect increases towards edges
const vigStrengthAtPixel = Math.pow(normDist, vignetteFalloff);
// Calculate multiplier for pixel brightness (1.0 at center, less towards edges)
const multiplier = 1.0 - (vignetteAmount * vigStrengthAtPixel);
r *= multiplier;
g *= multiplier;
b *= multiplier;
}
// 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));
// Alpha channel (data[i+3]) remains unchanged
}
// Put the modified image data back onto the canvas
ctx.putImageData(imageData, 0, 0);
return canvas;
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The ‘Image Ricoh GR Film Camera Filter Effect Application’ tool allows users to apply a stylized film camera filter effect to their images. By adjusting settings such as desaturation, contrast, film grain, vignette strength, and optional color tint, users can transform their photos to achieve a vintage or artistic look reminiscent of classic film photography. This tool is ideal for photographers, graphic designers, and social media enthusiasts looking to enhance their images with unique visual effects.