JavaScript: Image Processing

contain

What is an Image?

Images are Made of Pixels

  • Every digital image is a grid of tiny colored squares called pixels
  • A 1920x1080 image has over 2 million pixels!
  • Each pixel has a color value

How Pixels Store Color

Each pixel stores 4 values (RGBA):

Value Range Meaning
R 0-255 Red intensity
G 0-255 Green intensity
B 0-255 Blue intensity
A 0-255 Alpha (transparency)

Example Colors

// 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

Image Data as an Array

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)
]

Your Turn: Get the Starter Template

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.

Open it in your browser. You should see:

  • Two canvases (original + filtered)
  • Three buttons: Red Filter, Grayscale, Negative
  • A color picker that shows RGBA values on hover

We'll walk through the code together.

HTML5 Canvas

The <canvas> Element

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);

Drawing an Image on Canvas

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);
};

Getting Pixel Data

// 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

Creating Image Filters

The Loop Pattern

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);

Filter 1: Red Channel Only

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!

Filter 2: Grayscale

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;
}

Filter 3: Invert (Negative)

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!
}

Filter 4: Brightness

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?

Clamping Values

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);

Why This Matters

Real-World Applications

  • Photo editing apps (Instagram filters, Photoshop)
  • Computer vision (face detection, object recognition)
  • Image compression (reducing file size)
  • Medical imaging (enhancing X-rays, MRIs)
  • Security cameras (motion detection)

Understanding pixels is fundamental to many tech careers!

Key Concepts Practiced

  1. Arrays - Image data is stored as an array
  2. Loops - Process every pixel with a for loop
  3. Index math - Navigate array with i, i+1, i+2, i+3
  4. Functions - Create reusable filter functions
  5. DOM - Work with canvas elements

Practice

The Red filter already works. Now implement the other two:

  • Grayscale - average R, G, B and set all three to that value
  • Negative - subtract each value from 255

Done early? Try these:

  • Sepia - give the image a warm, brownish tone
  • Threshold - convert to pure black and white (no gray)
  • Channel swap - switch red and blue channels

References