Skip to content
CoronRing
All projects

Particle Wave

An image-to-particle-cloud pipeline with a real-time physics renderer. The same engine draws the corona on this site's home page.

Placeholder Particle Wave hero capture /media/projects/particle-wave/hero.webp

Live capture of the engine responding to the cursor.

Particle Wave · live demo running
0 pts · 0 fps

Starting engine…

Source
0.00 rad/s
360 °
8 px
60
120 px
350
2.40 × cursor
140
360 px/s
0 steps
1.00
0.65
2.60
4.20
2.00 px

Move the cursor to push the field, then click to fire the action bound to that button. A wave is a travelling front. It leaves the click point, kicks each particle once on the way past, and keeps going, so what you see is a ring crossing the cloud. A burst does not travel: it is a standing radial field that holds everything within its radius for as long as the button is down, hardest at the centre, so what you see is the cloud opening or gathering and then relaxing on release. An uploaded image goes to the ParticleWave service, which extracts the point cloud in Python and sends it back. If that service is unreachable, the image is traced in this tab instead, at lower quality. Nothing is stored either way. The cloud comes back in the response and the upload is discarded.

Placeholder write-up. Replace this body with the real account; the frontmatter above already carries the fields the layout renders.

The problem

A still image on a landing page is inert. A canvas of particles that responds to the cursor is not, but hand-authoring one per image does not scale.

The approach

Split the work in two. A Python pipeline turns any image into a sampled point cloud: grayscale, multi-scale edge extraction, then Poisson-disc sampling so points land where the detail is. The runtime knows nothing about images. It loads a cloud, spawns particles at rest positions, and simulates.

Because the two halves only share a serialisation contract, either can be replaced. The demo below proves it in both directions. Uploads go to the Python service and come back as a cloud the renderer accepts unchanged, and when that service is down a much simpler JavaScript tracer stands in without the renderer noticing. The corona on this site’s home page is not traced from an image at all. It is generated parametrically and loaded by the same runtime.

What I would do differently

To be written.

Run it here

The published package, installed from PyPI into this tab and executed by a real CPython build. Nothing runs on a server, and the first run downloads the interpreter.

Environment
Not started

The image-to-point-cloud extractor from PyPI, running in the tab. The same package the site’s demo backend imports, at the same version.

download
~32 MB extra on first run
packages
numpy · scipy · pillow · pyyaml · typer · click · rich · particle-wave
Source

Builds an image in memory, runs the full four-stage pipeline, and prints what came out.

Ctrl Enter to run · Tab indents the selection
Output

Nothing yet. Run the sample above.

Gallery

  • Placeholder Image → point cloud pipeline /media/projects/particle-wave/01.webp

    Edge extraction and density-aware sampling turn a source image into a cloud.

  • Placeholder Wave propagation /media/projects/particle-wave/02.webp

    A click spawns a wave packet that propagates outward and decays.