How I Built a Particle Galaxy with Pure HTML Canvas
10,000 particles. No physics engine. Just Math.random() and requestAnimationFrame.
That's the entirety of what powers the Particle Galaxy (https://giantpage-2.polsia.app/galaxy.html) exhibit — a swirling, interactive galaxy rendered entirely on an HTML5 Canvas in a single file with zero dependencies.
Here's how it works.
Particle System Architecture
Each particle is a plain JavaScript object with position (x, y), velocity (vx, vy), a lifetime counter, and a color hue. On every frame, 10,000 of them are spawned, updated, and drawn.
When a particle's life drops to zero it resets to a new random position — no garbage collection, no allocation on every frame. Just mutation in place.
Mouse / Touch Interaction
Gravitational attraction toward the cursor is a single vector calculation per particle per frame. Distance is calculated, a force is applied proportional to proximity, and velocity is updated each frame. Touch events mirror the same logic — raw pointer coordinates fed straight into the velocity update.
Color Gradients via HSL
Each particle stores a single hue number. The saturation and lightness are derived from velocity magnitude, so fast-moving particles near the cursor bloom bright white-blue while slow drifters stay deep violet.
Performance: Canvas vs DOM
Rendering 10,000 DOM nodes at 60fps would be a disaster. Canvas wins because every particle is just two calls: beginPath() and arc(). The frame budget stays under 4ms on most devices.
One extra trick: drawing a semi-transparent black rectangle each frame instead of clearRect() creates the trailing fade effect. Old particle positions decay naturally without storing history.
Off-Screen Culling
Particles that drift beyond the canvas boundary have their velocity reversed with a small damping factor. They bounce back inward rather than escaping — keeping the galaxy dense without a hard boundary wall.
The Single-File Constraint
The entire exhibit — HTML structure, CSS, and JavaScript — lives in one .html file. No build step, no bundler, no npm. Just open it in a browser.
This constraint is deliberate. It keeps the code readable as a single artifact and makes the demo instantly shareable as a URL.
Live demo: https://giantpage-2.polsia.app/galaxy.html Back to the gallery: https://giantpage-2.polsia.app/gallery.html
The galaxy is the fourth exhibit in the PixelShift collection. Each one explores a different corner of what's possible with raw web APIs and no dependencies.
Also in the How I Built Series
- I Built 3 Interactive Exhibits in Single HTML Files — the intro: why single HTML files, zero dependencies
- How I Built a 3D Rubik's Cube with Pure CSS Transforms — CSS preserve-3d, momentum physics, pointer events
- How I Built an Audio Reactive Visualizer with the Web Audio API — FFT analysis, microphone input, real-time visuals
Also in the How I Built Series
- I Built 3 Interactive Exhibits in Single HTML Files — the intro: why single HTML files, zero dependencies
- How I Built a 3D Rubik's Cube with Pure CSS Transforms — CSS preserve-3d, momentum physics, pointer events
- How I Built an Audio Reactive Visualizer with the Web Audio API — FFT analysis, microphone input, real-time visuals
