Every photo editor from Photoshop to Instagram filters does the same thing at the lowest level: read pixel values modify them and write them back. The HTML5 Canvas API gives you direct access to pixel data making browser-based photo editing genuinely powerful.
Reading pixel data
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = () => {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const pixels = imageData.data;
// pixels is a Uint8ClampedArray: [R, G, B, A, R, G, B, A, ...]
};
Every four consecutive values represent one pixel: red, green, blue, and alpha (transparency). A 1920x1080 image has 2,073,600 pixels and 8,294,400 values in the array.
Brightness adjustment
function adjustBrightness(imageData, amount) {
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
data[i] = Math.min(255, Math.max(0, data[i] + amount)); // R
data[i+1] = Math.min(255, Math.max(0, data[i+1] + amount)); // G
data[i+2] = Math.min(255, Math.max(0, data[i+2] + amount)); // B
// Alpha unchanged
}
return imageData;
}
Contrast adjustment
Contrast moves values away from (or toward) the midpoint (128):
function adjustContrast(imageData, factor) {
// factor > 1 increases contrast, < 1 decreases
const data = imageData.data;
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));
}
return imageData;
}
Grayscale conversion
The perceptually correct grayscale formula weights channels by human eye sensitivity:
function grayscale(imageData) {
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const gray = 0.299 * data[i] + 0.587 * data[i+1] + 0.114 * data[i+2];
data[i] = data[i+1] = data[i+2] = gray;
}
return imageData;
}
The weights (0.299, 0.587, 0.114) reflect that human eyes are most sensitive to green, then red, then blue. A simple average (1/3 each) produces visibly wrong results.
Crop, rotate, resize
These operations use canvas transformations rather than pixel manipulation:
// Crop: draw a portion of the image
ctx.drawImage(img, sx, sy, sWidth, sHeight, 0, 0, dWidth, dHeight);
// Rotate: transform the canvas context
ctx.translate(canvas.width/2, canvas.height/2);
ctx.rotate(angle * Math.PI / 180);
ctx.drawImage(img, -img.width/2, -img.height/2);
For editing photos directly in the browser with crop, rotate, brightness, contrast, and filters, I built an editor at zovo.one/free-tools/photo-editor. It uses Canvas API pixel manipulation and runs entirely client-side.
I'm Michael Lip. I build free developer tools at zovo.one. 500+ tools, all private, all free.
Top comments (0)