2D Canvas, Creative Coding & Particles
Immediate-mode rendering, particle systems, physics.
The DOM is great until you need a thousand things moving at once. When the count climbs into the
hundreds or thousands — sparks, snow, confetti, fluid, fields of agents — you drop down to a <canvas> and drive every pixel yourself. This is creative coding: a loop, an
array of particles, and a little physics.
Immediate mode vs retained mode
There are two ways a graphics system can work, and the distinction shapes everything you write.
You own the frame. Each tick you clear the canvas and redraw the entire scene from scratch. There are no persistent objects — just your data and draw calls. The raw Canvas 2D API works this way. Total control, zero scene-graph overhead, but you implement hit-testing, layering and dirty-checking yourself.
You build a scene graph of persistent objects (sprites, nodes) and the library decides what to redraw. SVG, the DOM, and PixiJS are retained. More convenient and often GPU-batched, but you pay memory and indirection for every object you keep around.
Particle systems
A particle system is just an array of structs, each with a position, a velocity, and a life that ticks toward zero. An emitter spawns new ones; forces mutate velocities; dead particles are culled or recycled. That's the whole pattern — every fireworks, smoke, or starfield effect is a variation on it.
- Emission — spawn N particles per second from a point/area, with randomized initial velocity.
- Forces — add to velocity each frame:
gravity(constant down),drag(multiply velocity by <1), anoise field(organic swirl), or attraction toward the cursor. - Integration —
pos += vel * dt(semi-implicit Euler) is the cheap default.
Sandbox 1 — Canvas particles
Raw Canvas 2D, a typed-array particle store, and an rAF loop with devicePixelRatio scaling. Push the max particles slider toward
3,000 and watch the FPS meter — additive blending plus flat Float32Arrays keep it
smooth. The cursor attracts or repels; clicking fires a burst.
Move the cursor over the canvas to pull/push particles. Click for a firework burst. Live: 0
Sandbox 2 — Matter.js rigid bodies
Particles are point masses; sometimes you need things that collide and tumble. Matter.js is a 2D rigid-body engine: you create an Engine, a World, static walls,
and dynamic bodies, then a Runner steps the simulation while Render draws
it. A MouseConstraint lets you grab and throw bodies. Tune restitution (bounciness)
and friction live.
Click empty space to add a box/circle. Drag to fling bodies around. Bodies: 0
Going GPU: PixiJS v8
The Canvas 2D API runs on the CPU; past a few thousand sprites it stalls. PixiJS is the standard retained-mode 2D renderer that batches sprites on the GPU, pushing
into the tens of thousands. v8 is a single package with an async init — you await app.init(...) — and ships both a WebGPU and a WebGL renderer with automatic fallback.
import { Application } from 'pixi.js';
const app = new Application();
await app.init({ preference: 'webgpu', resizeTo: window });
document.body.appendChild(app.canvas); Chrome/Edge ship WebGPU broadly; Safari 26 enabled it by default; Firefox is rolling out (still behind a flag on some platforms). PixiJS auto-falls back to WebGL, so it's safe to set preference:'webgpu' today.
Playground — edit the loop
This is the bare pattern, yours to break. Edit update() to flip gravity, add drag
(p.vx *= 0.98), or pull particles toward the center; edit draw() to
change size, color, or blending. The low-alpha clear paints motion trails — set it to 1 for crisp dots. It re-runs as you type.