Frontend Development 14 min read

Create Stunning Image Filters with Canvas: From Basics to Convolution

This tutorial explains how to implement common image filters such as red, grayscale, and inverse effects using the Canvas API, then introduces convolution fundamentals to achieve advanced effects like edge detection and sharpening, complete with code samples and visual results.

WecTeam
WecTeam
WecTeam
Create Stunning Image Filters with Canvas: From Basics to Convolution

Preface

Filters are used to achieve various image effects such as grayscale, color inversion, black‑white, mosaic, sharpening, etc. While Photoshop provides these functions, front‑end developers can implement them easily with the Canvas API. This article first shows simple filter examples, then introduces convolution basics for more complex effects.

1. Basics

1. Canvas API used in image processing

clearRect(x, y, width, height) – clears the specified rectangle.

drawImage(img, x, y, width, height) – draws an image onto the canvas.

getImageData(x, y, width, height) – returns pixel data of the specified rectangle.

putImageData(imgData, x, y) – puts image data back onto the canvas.

2. Processing flow

Code:

<code>&lt;canvas id="my_canvas"&gt;&lt;/canvas&gt;
// filter function
function filter(imageData, ctx) {
    // todo... process imageData
    return imageData;
}
let img = new Image();
img.src = "img.jpg";
img.onload = function () {
    let myCanvas = document.querySelector("#my_canvas");
    myCanvas.width = 400;
    myCanvas.height = 300;
    let myContext = myCanvas.getContext("2d");
    // draw image onto canvas
    myContext.drawImage(img, 0, 0, myCanvas.width, myCanvas.height);
    // get pixel data
    let imageData = myContext.getImageData(0, 0, myCanvas.width, myCanvas.height);
    // process pixel data
    imageData = filter(imageData, myContext);
    // put processed data back
    myContext.putImageData(imageData, 0, 0);
};</code>

The processing flow is simple, but how do we manipulate the pixel data?

3. Understanding pixel data

<code>// Get a 2x2 pixel block
let imageData = ctx.getImageData(0, 0, 2, 2);
console.log(imageData);
</code>

getImageData returns an

ImageData

object that contains a copy of the pixel data for the specified rectangle. Each pixel consists of four components (RGBA). The RGBA values are:

R – red (0‑255; 0 is black, 255 is pure red)

G – green (0‑255; 0 is black, 255 is pure green)

B – blue (0‑255; 0 is black, 255 is pure blue)

A – alpha (transparency) (0‑255; 0 is fully transparent, 255 is fully opaque)

2. Implementing filters

With pixel data understood, we can modify it inside the

filter

function. Below are three simple filters applied to an example image.

1. Red filter

Keeps the red channel unchanged while setting green and blue to 0.

<code>function filter(imageData, ctx) {
  let len = imageData.data.length / 4; // 4 values per pixel
  for (let i = 0; i < len; i++) {
    // imageData.data[i * 4 + 0] = red (unchanged)
    imageData.data[i * 4 + 1] = 0; // green
    imageData.data[i * 4 + 2] = 0; // blue
  }
  return imageData;
}
</code>

Result:

2. Grayscale filter

Sets R, G, B to the average of the three channels.

<code>function filter(imageData, ctx) {
  let len = imageData.data.length / 4;
  for (let i = 0; i < len; i++) {
    let newColor = (imageData.data[i*4] + imageData.data[i*4+1] + imageData.data[i*4+2]) / 3;
    imageData.data[i*4] = newColor;
    imageData.data[i*4+1] = newColor;
    imageData.data[i*4+2] = newColor;
  }
  return imageData;
}
</code>

Result:

3. Inverse filter

Replaces each channel with 255 minus its value.

<code>function filter(imageData, ctx) {
  let len = imageData.data.length / 4;
  for (let i = 0; i < len; i++) {
    imageData.data[i*4] = 255 - imageData.data[i*4];
    imageData.data[i*4+1] = 255 - imageData.data[i*4+1];
    imageData.data[i*4+2] = 255 - imageData.data[i*4+2];
  }
  return imageData;
}
</code>

Result:

These simple filters manipulate the four values of each pixel. More complex filters such as edge detection require convolution.

3. Convolution

Convolution is a common image‑processing technique that applies a kernel to each pixel to produce effects such as edge detection, sharpening, blur, emboss, etc.

1. Convolution process

Convolution uses a kernel (usually a 3×3 matrix) that is centered on each target pixel. The kernel’s elements are multiplied by the corresponding image pixel values, summed, and the result becomes the new pixel value.

When the kernel moves across the image, the output size shrinks (e.g., a 6×6 image with a 3×3 kernel yields a 4×4 output). Padding—adding a border of zeros—prevents size reduction.

2. Kernel characteristics

Size should be odd (e.g., 3×3, 5×5) so it has a central element.

Each element is a weight that determines the contribution of the corresponding pixel.

If the sum of all weights equals 1, overall brightness stays unchanged.

Sum > 1 brightens the image; sum < 1 darkens it; sum = 0 yields a very dark image.

3. Edge detection

An edge‑detection kernel typically has 8 in the center and -1 around it.

Applying this kernel highlights areas with strong color differences; uniform regions become black while edges appear bright.

<code>function convolutionMatrix(output, input, kernel) {
  let w = input.width, h = input.height;
  let iD = input.data, oD = output.data;
  for (let y = 1; y < h - 1; y++) {
    for (let x = 1; x < w - 1; x++) {
      for (let c = 0; c < 3; c++) {
        let i = (y * w + x) * 4 + c;
        oD[i] = kernel[0] * iD[i - w*4 - 4] +
                kernel[1] * iD[i - w*4] +
                kernel[2] * iD[i - w*4 + 4] +
                kernel[3] * iD[i - 4] +
                kernel[4] * iD[i] +
                kernel[5] * iD[i + 4] +
                kernel[6] * iD[i + w*4 - 4] +
                kernel[7] * iD[i + w*4] +
                kernel[8] * iD[i + w*4 + 4];
      }
      oD[(y * w + x) * 4 + 3] = 255; // alpha
    }
  }
  return output;
}
function filter(imageData, ctx) {
  let kernel = [-1,-1,-1,-1,8,-1,-1,-1,-1]; // edge‑detection kernel
  return convolutionMatrix(ctx.createImageData(imageData), imageData, kernel);
}
</code>

Using a different kernel yields other effects, such as sharpening with a central value of 9.

<code>let kernel = [-1,-1,-1,-1,9,-1,-1,-1,-1]; // sharpening kernel</code>

4. Summary

Image processing with Canvas is fun; you can also capture video from the camera via

navigator.mediaDevices

, apply filters in real time, and draw the result back to the canvas. Understanding convolution opens the door to neural networks and deep learning, where convolution is a fundamental operation.

Demo

https://wqs.jd.com/demo/filter/index.html

javascriptFrontend DevelopmentcanvasConvolutionImage Filtering
WecTeam
Written by

WecTeam

WecTeam (维C团) is the front‑end technology team of JD.com’s Jingxi business unit, focusing on front‑end engineering, web performance optimization, mini‑program and app development, serverless, multi‑platform reuse, and visual building.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.