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.
Mandelbrot Set
Advanced Mandelbrot set renderer with smooth (continuous) iteration colouring — animates a slow zoom into the Seahorse Valley. Wikipedia
// Escape-time algorithm
const mandelbrot = (cx: number, cy: number): number => {
let zx = 0, zy = 0;
for (let i = 0; i < maxIter; i++) {
const zx2 = zx * zx;
const zy2 = zy * zy;
if (zx2 + zy2 > 4) {
// Smooth iteration count (eliminates banding)
return i + 1 - Math.log2(Math.log2(
Math.sqrt(zx2 + zy2)
));
}
zy = 2 * zx * zy + cy;
zx = zx2 - zy2 + cx;
}
return 0; // inside the set
};
// Map screen pixel → complex plane
const render = () => {
const imageData = ctx.createImageData(W, H);
const d = imageData.data;
for (let py = 0; py < H; py++) {
for (let px = 0; px < W; px++) {
const cx = (px - W/2) / zoom + centerX;
const cy = (py - H/2) / zoom + centerY;
const t = mandelbrot(cx, cy);
const [r, g, b] = iterToColor(t);
const i = (py * W + px) * 4;
d[i]=r; d[i+1]=g; d[i+2]=b; d[i+3]=255;
}
}
ctx.putImageData(imageData, 0, 0);
};
// Animate zoom
const step = () => {
zoom *= 1.015;
if (zoom > 80000) zoom = 150; // reset
render();
requestAnimationFrame(step);
};Key Concepts
- The Mandelbrot set: all complex numbers c for which z²+c does not escape to infinity
- Escape condition:
|z|² > 4— avoids the expensiveMath.sqrt - Smooth colouring:
i + 1 - log₂(log₂(|z|))removes integer banding artifacts - All rendering goes through
ImageData— writing to aUint8ClampedArrayis much faster than per-pixelfillRect - Zoom: divide screen coordinates by
zoomfactor to map to the complex plane - For interactive zooming: move
render()to a Web Worker to keep the UI responsive