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.
Flow Field
Advanced 1,200 particles following a dynamic vector field derived from layered sine/cosine noise — a Perlin noise substitute.
// Smooth noise via layered trigonometry
const noise = (x: number, y: number, t: number) =>
Math.sin(x * 0.006 + t * 0.8) *
Math.cos(y * 0.007 + t * 0.5) +
Math.sin(x * 0.013 - t * 0.4) *
Math.cos(y * 0.011 + t * 0.3) * 0.5 +
Math.sin(x * 0.025 + y * 0.015 + t * 1.1) * 0.25;
interface Agent { x: number; y: number; hue: number; }
const agents: Agent[] = Array.from({ length: 1200 },
() => ({ x: Math.random()*W, y: Math.random()*H,
hue: Math.random()*360 }));
let t = 0;
const step = () => {
// Very slow fade — trails persist for many frames
ctx.fillStyle = 'rgba(10, 10, 30, 0.04)';
ctx.fillRect(0, 0, W, H);
for (const a of agents) {
// Sample noise → direction angle
const angle = noise(a.x, a.y, t) * Math.PI * 2.5;
const speed = 1.5;
a.x += Math.cos(angle) * speed;
a.y += Math.sin(angle) * speed;
// Toroidal wrap
if (a.x < 0) a.x = W; if (a.x > W) a.x = 0;
if (a.y < 0) a.y = H; if (a.y > H) a.y = 0;
a.hue = (a.hue + 0.3) % 360;
ctx.beginPath();
ctx.arc(a.x, a.y, 1, 0, Math.PI * 2);
ctx.fillStyle = `hsla(${a.hue}, 90%, 65%, 0.7)`;
ctx.fill();
}
t += 0.004;
requestAnimationFrame(step);
};Key Concepts
- Each particle reads the noise value at its position to get a steering angle
- Layered sine/cosine at different frequencies approximates Perlin noise cheaply
- The
toffset evolves the field over time — field appears to flow - Extremely slow fade (
alpha 0.04) produces long, dense trailing streaks - HSL colour with incrementing hue gives each particle a rainbow trail
- For production: use a precomputed Perlin/Simplex noise grid sampled by position