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.
Particle System
Advanced Physics-based particles with buoyancy, turbulence, and additive (lighter) blending for a glowing fire-fountain effect.
interface Particle {
x: number; y: number;
vx: number; vy: number;
life: number; // 1 → 0
decay: number;
size: number;
hue: number;
}
const step = () => {
// Semi-transparent dark overlay = fade trail
ctx.fillStyle = 'rgba(10, 10, 30, 0.15)';
ctx.fillRect(0, 0, W, H);
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
// Physics
p.vy -= 0.06; // buoyancy (upward)
p.vx += (Math.random() - 0.5) * 0.2; // turbulence
p.vx *= 0.99; // drag
p.vy *= 0.99;
p.x += p.vx;
p.y += p.vy;
p.life -= p.decay;
if (p.life <= 0) { particles.splice(i, 1); continue; }
// Additive blending — particles add together = glow
ctx.globalCompositeOperation = 'lighter';
ctx.beginPath();
ctx.arc(p.x, p.y, p.size * p.life, 0, Math.PI * 2);
ctx.fillStyle = `hsla(${p.hue}, 100%, 60%, ${p.life})`;
ctx.fill();
ctx.globalCompositeOperation = 'source-over';
}
requestAnimationFrame(step);
};
// Spawn particles
const emit = (ex: number, ey: number) => {
const angle = -Math.PI/2 + (Math.random()-0.5) * 1.2;
const speed = 2 + Math.random() * 3;
particles.push({
x: ex, y: ey,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
life: 1,
decay: 0.008 + Math.random() * 0.006,
size: 3 + Math.random() * 4,
hue: baseHue + Math.random() * 60,
});
};Key Concepts
- Each particle stores
life(0→1) decremented bydecayeach frame - Upward buoyancy: subtract a small constant from
vyevery frame - Turbulence: add a small random value to
vxeach frame globalCompositeOperation = 'lighter'adds RGB values — overlapping particles glow- Semi-transparent clear (
rgba(…, 0.15)) fades the trail gradually - Iterate backward (
for i = length-1; i >= 0; i--) when splicing to avoid index skipping