Image Adjustment in the Browser — Brightness, Contrast, Saturation Without Libraries

CSS filter handles simple visual effects. For pixel-level control — downloadable output, real adjustment values — you need Canvas and pixel manipulation.

Getting Pixel Data

async function getImageData(file) { const img = await loadImage(file); const canvas = document.createElement('canvas'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; canvas.getContext('2d').drawImage(img, 0, 0); return canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height); } 

Brightness

Brightness adds or subtracts from each channel:

function applyBrightness(data, value) { // value: -100 to +100 const factor = value * 2.55; for (let i = 0; i < data.length; i += 4) { data[i] = Math.min(255, Math.max(0, data[i] + factor)); data[i + 1] = Math.min(255, Math.max(0, data[i + 1] + factor)); data[i + 2] = Math.min(255, Math.max(0, data[i + 2] + factor)); } } 

Contrast

Contrast expands or compresses the range around the midpoint (128):

function applyContrast(data, value) { // value: -100 to +100 const factor = (259 * (value + 255)) / (255 * (259 - value)); for (let i = 0; i < data.length; i += 4) { data[i] = Math.min(255, Math.max(0, factor * (data[i] - 128) + 128)); data[i + 1] = Math.min(255, Math.max(0, factor * (data[i + 1] - 128) + 128)); data[i + 2] = Math.min(255, Math.max(0, factor * (data[i + 2] - 128) + 128)); } } 

Saturation

Saturation requires converting to HSL, adjusting S, and converting back:

function rgbToHsl(r, g, b) { r /= 255; g /= 255; b /= 255; const max = Math.max(r, g, b), min = Math.min(r, g, b); let h, s, l = (max + min) / 2; if (max === min) { h = s = 0; } else { const d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch (max) { case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break; case g: h = ((b - r) / d + 2) / 6; break; case b: h = ((r - g) / d + 4) / 6; break; } } return [h, s, l]; } 

Sharpness (Unsharp Mask)

Sharpness isn't a simple per-pixel operation — it requires a convolution:

function applySharpness(imageData, strength) { const kernel = [ 0, -strength, 0, -strength, 1 + 4 * strength, -strength, 0, -strength, 0 ]; return convolve(imageData, kernel); } 

Performance

Processing a 4000×3000 image (48MB of raw pixel data) takes ~200ms in JavaScript. For a preview, work at 1/4 resolution and only process full resolution on download.

Adjust images at toolzip.app/tools/image-adjust.