Each pixel stores 4 values (RGBA):
// Pure red R: 255, G: 0, B: 0, A: 255 // Pure green R: 0, G: 255, B: 0, A: 255 // White (all colors at max) R: 255, G: 255, B: 255, A: 255 // Black (no colors) R: 0, G: 0, B: 0, A: 255 // Gray (equal amounts) R: 128, G: 128, B: 128, A: 255
For a 2x2 image (4 pixels), the data array has 16 values:
// [R, G, B, A, R, G, B, A, ...] [ 255, 0, 0, 255, // Pixel 1 (red) 0, 255, 0, 255, // Pixel 2 (green) 0, 0, 255, 255, // Pixel 3 (blue) 255, 255, 0, 255 // Pixel 4 (yellow) ]
Download the starter template (right-click > Save As).
Save it in your oim3690 repo along with an images/ folder containing any .jpg image renamed to tiger1.jpg.
oim3690
images/
.jpg
tiger1.jpg
Open it in your browser. You should see:
We'll walk through the code together.
<canvas>
Canvas lets us draw and manipulate images with JavaScript:
<canvas id="myCanvas" width="400" height="300"></canvas>
const canvas = document.getElementById('myCanvas'); const ctx = canvas.getContext('2d'); // Now we can draw on the canvas! ctx.fillStyle = 'red'; ctx.fillRect(10, 10, 100, 50);
const canvas = document.getElementById('myCanvas'); const ctx = canvas.getContext('2d'); const img = new Image(); img.src = 'photo.jpg'; img.onload = function() { // Draw the image when it's loaded ctx.drawImage(img, 0, 0); };
// Get all pixel data from the canvas const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const pixels = imageData.data; // pixels is an array: [R, G, B, A, R, G, B, A, ...] console.log(pixels.length); // width * height * 4 // Access first pixel const r = pixels[0]; // Red const g = pixels[1]; // Green const b = pixels[2]; // Blue const a = pixels[3]; // Alpha
To process every pixel, we loop through the data array:
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const pixels = imageData.data; // Loop through every pixel (4 values at a time) for (let i = 0; i < pixels.length; i += 4) { const r = pixels[i]; // Red const g = pixels[i + 1]; // Green const b = pixels[i + 2]; // Blue const a = pixels[i + 3]; // Alpha // Modify the pixel here... } // Put the modified data back ctx.putImageData(imageData, 0, 0);
Keep only the red values, set green and blue to 0:
for (let i = 0; i < pixels.length; i += 4) { // pixels[i] stays the same (red) pixels[i + 1] = 0; // Remove green pixels[i + 2] = 0; // Remove blue // pixels[i + 3] stays the same (alpha) }
Try this with green channel or blue channel!
Convert to grayscale by averaging RGB values:
for (let i = 0; i < pixels.length; i += 4) { const r = pixels[i]; const g = pixels[i + 1]; const b = pixels[i + 2]; // Calculate average const gray = (r + g + b) / 3; // Set all channels to the same value pixels[i] = gray; pixels[i + 1] = gray; pixels[i + 2] = gray; }
Subtract each value from 255:
for (let i = 0; i < pixels.length; i += 4) { pixels[i] = 255 - pixels[i]; // Invert red pixels[i + 1] = 255 - pixels[i + 1]; // Invert green pixels[i + 2] = 255 - pixels[i + 2]; // Invert blue // Don't invert alpha! }
Increase or decrease all values:
const brightness = 50; // Positive = brighter, negative = darker for (let i = 0; i < pixels.length; i += 4) { pixels[i] = pixels[i] + brightness; pixels[i + 1] = pixels[i + 1] + brightness; pixels[i + 2] = pixels[i + 2] + brightness; }
What happens if a value goes above 255 or below 0?
Keep values in valid range (0-255):
function clamp(value) { if (value < 0) return 0; if (value > 255) return 255; return value; } // Use it in your filter pixels[i] = clamp(pixels[i] + brightness);
Understanding pixels is fundamental to many tech careers!
for
i
i+1
i+2
i+3
The Red filter already works. Now implement the other two:
Done early? Try these: