WebGL is a JavaScript API that enables rendering high-performance 2D and 3D graphics directly in the browser using the GPU. In this article, we'll walk through building an image filter (like grayscale or blur) using WebGL shaders and JavaScript. This allows for real-time processing and smooth performance, even with large images.
1. Why Use WebGL for Image Filtering?
Traditional image manipulation using Canvas or JavaScript loops is CPU-bound and slow for high-res images. WebGL leverages GPU shaders to handle pixel-level transformations in parallel, delivering speed and responsiveness crucial for modern UI effects and photo editors.
2. Basic HTML Setup
Start with a simple page layout and an image to filter:
<canvas id="glcanvas" width="500" height="500"></canvas>
<img id="source" src="image.jpg" crossorigin="anonymous" style="display:none;" />
3. WebGL Shader Basics
You’ll need a vertex shader and a fragment shader. The vertex shader maps the image onto a rectangle, and the fragment shader processes each pixel.
const vertexShaderSource = `
attribute vec2 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
void main() {
gl_Position = vec4(a_position, 0, 1);
v_texCoord = a_texCoord;
}`;
const fragmentShaderSource = `
precision mediump float;
varying vec2 v_texCoord;
uniform sampler2D u_image;
void main() {
vec4 color = texture2D(u_image, v_texCoord);
float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
gl_FragColor = vec4(vec3(gray), 1.0);
}`;
4. Initializing WebGL Context
Set up the WebGL rendering context and compile shaders:
const canvas = document.getElementById('glcanvas');
const gl = canvas.getContext('webgl');
function createShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
return shader;
}
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
5. Rendering the Image with Filter
Create and link the WebGL program, load the image texture, and draw the filtered output:
function createProgram(gl, vShader, fShader) {
const program = gl.createProgram();
gl.attachShader(program, vShader);
gl.attachShader(program, fShader);
gl.linkProgram(program);
return program;
}
const program = createProgram(gl, vertexShader, fragmentShader);
gl.useProgram(program);
const positionLocation = gl.getAttribLocation(program, "a_position");
const texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1, -1, 1, -1, -1, 1,
-1, 1, 1, -1, 1, 1,
]), gl.STATIC_DRAW);
Then load the image and draw:
const image = document.getElementById('source');
image.onload = () => {
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA,
gl.UNSIGNED_BYTE, image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.drawArrays(gl.TRIANGLES, 0, 6);
};
6. Going Further
- Add a UI to switch filters (blur, sepia, invert, etc.)
- Support video textures for real-time webcam effects
- Export the filtered image using
canvas.toDataURL()
Conclusion
WebGL is a powerful tool for building high-performance, interactive visual effects in the browser. This simple grayscale filter can be extended into a full-featured image editor or dynamic UI component, all running smoothly thanks to GPU acceleration.
If this post helped you, consider supporting me here: buymeacoffee.com/hexshift
Top comments (0)