Particles colliding with live text
Simulating point particles colliding with live-rendered canvas text using a pixel-derived collision matrix.
A recent subject I’ve been fascinated by is collision detection in graphics and real-time animation, especially how this can be applied to web-based graphics. Here you can see partile collisions taking place against a standard web font, rendered onto an HTML <canvas> element.
2026 update The CodePen embed is being re-hosted here; the interactive version will return.
Collision detection is the process of determining, for any given object in a scene, how that object interacts with other objects in the scene. Realistic animation effects can then be applied to give the illusion of real-world physical movement – i.e. snowflakes settling on a surface, or the sparks emitted by a burning wood fire.
In the case of this code sample, I was keen to investigate how I could simulate the collision between point particles, and a portion of text entered into an input box. I specifically wanted to be able to dynamically adjust the font and text used in the scene, as well as the positioning and velocity of the particles emmitted into the scene.
Feel free to adjust some of the animation’s parameters using the tools below the animation. You can adjust the text, size and choice of font to see how this affects the particles and their motion relative to the text.
In order to simulate the particle collisions, I had to generate a collision matrix for each part of the text, so that each particle could refer to this matrix
The pixel data for the font is grabbed directly from the canvas via the getImageData() method. This provides an array containing the RGBA value of every single pixel on the canvas area. This data is then processed to determine which parts of the canvas are either "font" or "not font".
This animation shows only a very basic implementation of this matrix, and there’s plenty room for improvement, technically speaking – but from an aesthetic point of view, you can see the particles interacting cleanly with the font. The effect is pleasing, although not physically accurate (the code simply states "If particle collides with font, send particle heading in another random direction until particle settles at zero velocity" which looks nice enough)
Some performance optimisations have been made. For example, because we know in advance that the text only takes up a specific (and calculatable) portion of the center of the page, we don’t need to carry out any hit-calculations for the other empty portions. We know that no particles will interact with text in these areas.
Also, the hit matrix can be calculated using a less precise box-based grid. So, instead of calculating hit-vs-miss for every single pixel, the entire canvas area can be subdivided using larger squares. e.g. we can then say, "For any given 4-pixel by 4-pixel area, does this area contain part of a font? If so, process the particle collision code"
2026 update If you call getImageData() repeatedly to rebuild the collision matrix, grab the context with willReadFrequently: true — browsers otherwise assume a write-mostly GPU-backed canvas and pay a readback penalty on every call:
const ctx = canvas.getContext('2d', { willReadFrequently: true });
The RGBA buffer comes back as a Uint8ClampedArray, so the “font vs not-font” pass is a tight typed-array loop. And the whole simulation moves cleanly onto a Web Worker via OffscreenCanvas, keeping the main thread free for the input controls.
For all intents and purposes, if the focus of the animation is on visual appearance rather than functional accuracy, then I would feel that further development of the algorithm is unecessary.
This is an interesting consideration, one of time-vs-benefit. A lot of time can be spent on visual effects, or complex features, but – based on the laws of ever diminishing returns – more time spent doesn’t necessarily equate to a better visual effect.
This carries over to my work in web development too. There’s no point spending 50% more time on a complex feature if that extra time won’t necessarily equate to an improvement in visual "niceness" from a visitor’s point of view.
That’s another important thing I’ve picked up while working on these personal projects – knowing when to stop!
Of course, for the sake of completeness and my own curiosity, I’ll doubtless come back to this at some future date and improve upon the code, possible enhancements being:
I help content-rich organisations understand their data and pull it back into one place. Tell me what specific problems you're having and I'll be happy to talk solutions with you.
Tell me what specific problems you’re having and I’ll be happy to talk solutions with you.