Canvas and WebGL are the browser's APIs for 2D and 3D graphics rendering. Firefox has strong support for both, plus excellent DevTools for inspecting canvas operations. This guide covers Canvas 2D for practical tasks and WebGL for developers getting into GPU-accelerated graphics.
Canvas 2D
The <canvas> element provides a bitmap drawing surface. You draw on it with the 2D rendering context API.
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// Size the canvas (not via CSS — CSS just scales pixels)
canvas.width = 800;
canvas.height = 600;
// Draw a rectangle
ctx.fillStyle = '#0066cc';
ctx.fillRect(50, 50, 200, 100);
// Draw a circle
ctx.beginPath();
ctx.arc(400, 300, 50, 0, Math.PI * 2);
ctx.fillStyle = 'red';
ctx.fill();
// Draw text
ctx.font = '24px sans-serif';
ctx.fillStyle = '#000';
ctx.fillText('Hello Canvas', 300, 200);
Canvas for Image Processing
// Load an image and draw it
const img = new Image();
img.onload = () => {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
// Get pixel data
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data; // Uint8ClampedArray: [R, G, B, A, R, G, B, A, ...]
// Grayscale filter
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i+1] + data[i+2]) / 3;
data[i] = avg; // R
data[i+1] = avg; // G
data[i+2] = avg; // B
// data[i+3] is alpha — leave it
}
ctx.putImageData(imageData, 0, 0);
};
img.src = '/photo.jpg';
Firefox privacy consideration: cross-origin images make the canvas "tainted" — getImageData() throws a SecurityError for tainted canvases. This prevents cross-origin pixel data theft. Same-origin or CORS-enabled images don't have this restriction.
Animation Loop
function animate(timestamp) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Update and draw
drawBackground(ctx);
drawSprites(ctx, timestamp);
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
requestAnimationFrame syncs to the display refresh rate (typically 60Hz or 120Hz). Better than setInterval for animation — pauses when the tab is backgrounded (saves CPU/battery) and handles high-refresh displays.
WebGL: GPU-Accelerated Graphics
WebGL exposes OpenGL ES 2.0/3.0 to JavaScript. It's more complex than Canvas 2D but handles millions of vertices and effects that would be impossibly slow in Canvas 2D.
const gl = canvas.getContext('webgl2'); // WebGL 2 = OpenGL ES 3.0
// Clear to a color
gl.clearColor(0.1, 0.1, 0.2, 1.0); // RGBA (0-1 range)
gl.clear(gl.COLOR_BUFFER_BIT);
Shaders are GPU programs written in GLSL. WebGL requires at minimum a vertex shader and a fragment shader:
// Vertex shader — runs for each vertex
const vertexShaderSource = `
attribute vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0.0, 1.0);
}
`;
// Fragment shader — runs for each pixel
const fragmentShaderSource = `
precision mediump float;
uniform vec4 u_color;
void main() {
gl_FragColor = u_color;
}
`;
// Compile shaders
function compileShader(gl, source, type) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error('Shader error:', gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
const vertShader = compileShader(gl, vertexShaderSource, gl.VERTEX_SHADER);
const fragShader = compileShader(gl, fragmentShaderSource, gl.FRAGMENT_SHADER);
const program = gl.createProgram();
gl.attachShader(program, vertShader);
gl.attachShader(program, fragShader);
gl.linkProgram(program);
gl.useProgram(program);
Three.js for Practical 3D
Three.js abstracts WebGL complexity into a usable API:
import * as THREE from 'three';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('three-canvas') });
renderer.setSize(window.innerWidth, window.innerHeight);
// Create a box
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x0066cc });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 5;
function animate() {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
Three.js works in Firefox — WebGL hardware acceleration is enabled by default.
Firefox DevTools for Canvas Debugging
Firefox's DevTools Canvas panel (enable in DevTools Settings → Canvas) records draw calls:
- Open DevTools → Canvas
- Click Record to start capturing
- Perform the canvas operation
- Stop recording
The recorded calls show:
- Every drawing operation in order
- The canvas state at each step
- Which JavaScript called the draw operation
This is invaluable for finding out why something is drawn in the wrong order, wrong color, or wrong position.
WebGL in Firefox: Hardware Acceleration Check
about:support → Graphics section → WebGL Renderer
Should show your GPU name (e.g., "ANGLE (Intel, Intel(R) HD Graphics 620 Direct3D11)"). If it shows "Software rendering" or "Mesa Software Rasterizer", hardware acceleration is off.
Enable hardware acceleration: Settings → General → Performance → Use hardware acceleration when available.
Canvas Performance in Firefox
Avoid reading pixels in hot paths: getImageData() is slow — it forces a GPU-to-CPU synchronization. Read pixels only when necessary, not every frame.
Use willReadFrequently hint for canvases that frequently read pixels:
const ctx = canvas.getContext('2d', { willReadFrequently: true });
This tells Firefox to keep the bitmap in CPU-accessible memory, making getImageData() faster at the cost of GPU compositing performance.
OffscreenCanvas for worker-based rendering:
const offscreen = canvas.transferControlToOffscreen();
worker.postMessage({ canvas: offscreen }, [offscreen]);
// In worker:
const ctx = canvas.getContext('2d');
// Render in background thread
Top comments (0)