Code
Canvas
Basic Shapes
fillRect, strokeRect, arc, closePath and roundRect — the core drawing primitives.
Colors & Gradients
Linear, radial and conic gradients, drop shadows, and globalAlpha transparency.
Text Rendering
fillText, strokeText, font styles, textAlign, textBaseline, and measureText.
Transformations
translate, rotate, scale, setTransform and the save/restore state stack.
Bezier Paths
Quadratic and cubic Bézier curves, Path2D reuse, star polygons, and arcTo.
Animation Loop
requestAnimationFrame-driven bouncing balls with gravity, friction, and motion-blur trails.
Mouse Drawing
Interactive paint application — smooth draw/erase with brush colour and size controls.
Compositing Modes
All 16 globalCompositeOperation modes — from source-over to multiply and xor.
Pixel Manipulation
ImageData filters — grayscale, invert, sepia, brightness boost, and pixelate.
Particle System
Physics-based particles with buoyancy, turbulence, colour lifecycle, and additive blending.
Fractal Tree
Animated recursive fractal tree — dynamic branching angle with brown-to-green colour transition.
Conway's Game of Life
Cellular automaton on a toroidal grid — pause, resume, and randomise the population.
Flow Field
1,200 particles following a dynamic vector field built from layered sine/cosine noise.
Mandelbrot Set
Mandelbrot set with smooth colouring, auto-zooming into the Seahorse Valley.
Generative Art
Algorithmic tile grid — noise-driven hue and rotation with layered concentric ring overlays.
Pixel Manipulation
Intermediate Direct per-pixel access via ImageData — grayscale, invert, sepia, brightness, and pixelate filters in pure TypeScript. MDN Reference
// Get raw pixel data
const imgData = ctx.getImageData(0, 0, W, H);
const d = imgData.data; // Uint8ClampedArray
// d = [R,G,B,A, R,G,B,A, ...]
// Grayscale (luminance formula)
for (let i = 0; i < d.length; i += 4) {
const lum = 0.2126 * d[i]
+ 0.7152 * d[i+1]
+ 0.0722 * d[i+2];
d[i] = d[i+1] = d[i+2] = lum;
}
ctx.putImageData(imgData, 0, 0);
// Invert
for (let i = 0; i < d.length; i += 4) {
d[i] = 255 - d[i];
d[i+1] = 255 - d[i+1];
d[i+2] = 255 - d[i+2];
}
// Sepia
const r = d[i], g = d[i+1], b = d[i+2];
d[i] = Math.min(255, r*.393 + g*.769 + b*.189);
d[i+1] = Math.min(255, r*.349 + g*.686 + b*.168);
d[i+2] = Math.min(255, r*.272 + g*.534 + b*.131);
// Brightness ×1.5
d[i] = Math.min(255, d[i] * 1.5);
d[i+1] = Math.min(255, d[i+1] * 1.5);
d[i+2] = Math.min(255, d[i+2] * 1.5);
// Pixelate — sample top-left of block
const px = 10;
for (let y = 0; y < H; y += px) {
for (let x = 0; x < W; x += px) {
const base = (y * W + x) * 4;
// fill entire px×px block with same colour
}
}Key APIs
ctx.getImageData(x, y, w, h)— reads pixels into aUint8ClampedArrayctx.putImageData(imageData, x, y)— writes pixels back to the canvasctx.createImageData(w, h)— allocates a blank pixel buffer- Pixel index:
(row × width + col) × 4→[R, G, B, A] - Values are clamped to 0–255 automatically by
Uint8ClampedArray - For large images, use an
OffscreenCanvaswith a Web Worker to avoid blocking the main thread