You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg, saturationLevel = 0.6, contrastLevel = 1.3, tintColor = "#E09040", tintOpacity = 0.25, noiseIntensity = 15) {
const canvas = document.createElement('canvas');
// Using { willReadFrequently: true } can sometimes optimize frequent getImageData/putImageData calls
const ctx = canvas.getContext('2d', { willReadFrequently: true });
// Ensure originalImg is loaded and has dimensions
const imgWidth = originalImg.naturalWidth || originalImg.width;
const imgHeight = originalImg.naturalHeight || originalImg.height;
if (imgWidth === 0 || imgHeight === 0) {
console.warn("Image has zero dimensions. Returning an empty canvas.");
// Create a minimal canvas to avoid errors downstream if an empty one is not handled
canvas.width = 1;
canvas.height = 1;
return canvas;
}
canvas.width = imgWidth;
canvas.height = imgHeight;
ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
// Helper function to parse hex color string (e.g., "#F00", "#FF0000") to {r, g, b}
function hexToRgb(hex) {
if (!hex || typeof hex !== 'string') return null;
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b);
const 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;
}
let parsedTintColor = hexToRgb(tintColor);
if (!parsedTintColor) {
console.warn(`Invalid tintColor: "${tintColor}". Attempting to use default tint #E09040.`);
// If the provided tintColor is invalid, try parsing the default value.
// This makes the tint robustly applied unless the hardcoded default is also malformed.
if (tintColor !== "#E09040") { // Avoid infinite loop if default itself is the problem
parsedTintColor = hexToRgb("#E09040");
}
if (!parsedTintColor) {
console.error("Default tintColor #E09040 could not be parsed. Tint will be skipped.");
}
}
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
// Alpha channel (data[i+3]) is typically preserved
// 1. Desaturation
// saturationLevel = 1 means original color, 0 means grayscale.
// Lerp: gray * (1 - saturationLevel) + colorComponent * saturationLevel
const gray = 0.299 * r + 0.587 * g + 0.114 * b;
r = gray * (1 - saturationLevel) + r * saturationLevel;
g = gray * (1 - saturationLevel) + g * saturationLevel;
b = b * (1 - saturationLevel) + b * saturationLevel;
// Clamp values (though lerp with saturationLevel in [0,1] should keep them in [0,255] if originals are)
r = Math.max(0, Math.min(255, r));
g = Math.max(0, Math.min(255, g));
b = Math.max(0, Math.min(255, b));
// 2. Contrast
// contrastLevel = 1.0 means no change.
// Formula: NewValue = (((OldValue / 255.0) - 0.5) * contrastFactor) + 0.5) * 255.0;
// This pushes values away from (contrastLevel > 1) or pulls them towards (contrastLevel < 1) the midpoint 128.
if (contrastLevel !== 1.0) {
r = ((r / 255.0 - 0.5) * contrastLevel + 0.5) * 255.0;
g = ((g / 255.0 - 0.5) * contrastLevel + 0.5) * 255.0;
b = ((b / 255.0 - 0.5) * contrastLevel + 0.5) * 255.0;
}
// Clamp values after contrast adjustment
r = Math.max(0, Math.min(255, r));
g = Math.max(0, Math.min(255, g));
b = Math.max(0, Math.min(255, b));
// 3. Tint
// Blend current pixel color with parsedTintColor based on tintOpacity
if (parsedTintColor && tintOpacity > 0) {
r = r * (1 - tintOpacity) + parsedTintColor.r * tintOpacity;
g = g * (1 - tintOpacity) + parsedTintColor.g * tintOpacity;
b = b * (1 - tintOpacity) + parsedTintColor.b * tintOpacity;
}
// Clamp values after tinting
r = Math.max(0, Math.min(255, r));
g = Math.max(0, Math.min(255, g));
b = Math.max(0, Math.min(255, b));
// 4. Noise
if (noiseIntensity > 0) {
// Add noise symmetrically around 0; noiseIntensity defines the max deviation.
// Math.random() gives [0, 1), so (Math.random() - 0.5) gives [-0.5, 0.5).
// Multiply by 2 to get [-1, 1), then by noiseIntensity.
const noise = (Math.random() - 0.5) * noiseIntensity * 2;
r += noise;
g += noise;
b += noise;
}
// Final clamp and round to integer for pixel data
data[i] = Math.max(0, Math.min(255, Math.round(r)));
data[i + 1] = Math.max(0, Math.min(255, Math.round(g)));
data[i + 2] = Math.max(0, Math.min(255, Math.round(b)));
// data[i+3] (alpha) remains unchanged
}
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 Construction Site Filter Effect Tool allows users to apply a unique filter effect to images, enhancing them with controls for saturation, contrast, tint color, tint opacity, and noise intensity. This tool is particularly useful for creative projects where a specific aesthetic is desired, such as in digital art, promotional materials, or social media content. Users can easily modify their images to reflect an industrial or construction-themed look, making it ideal for architects, designers, and anyone looking to add a distinctive flair to their visual content.