Gallery
Motivation
Physics solvers tend to be very complex software. In the
past, I toyed around with physics solvers for different kinds of geometries, but
struggled to implement these algorithms in a way that was both convincing
and performant. The idea for Fishbowl came from a video by the creator
Pezzza. They describe the high-level implementation details of a Verlet
Integration solver. This approach caught my interest.
Pezza's work is very impressive, particularly the demonstration at the end
which proves the program is deterministic. However, the focus of Pezzza's
project was to create a performant real-time application. I was instead
interested in pre-rendered images and animations. I decided to expand the
one-off demonstration at the end of Pezzza's video into a fully fledged
app.
Approach
At the center of any physics solver is the physics 'step', a fixed interval of
time where objects move and collide. The physics step of Fishbowl consists of five
distinct phases.
self.constrain_rect();
self.sort();
self.collide();
self.integrate();
self.clock += 1;
Because the program doesn't run in real time, simulating over one second
may take more or less than a second. Calculating a larger number of shorter
time steps will make the simulation more accurate, but will take longer to
process. Fishbowl uses an number of optimizations to reduce processing time.
By keeping the array of objects sorted from left to right, the number of collision
checks is significantly reduced. The rendering system is decoupled from the simulation,
and the two are pipelined together and run in parallel.
Rendering
The method I used for drawing circles to a frame went through numerous iterations.
Rendering performance is a major bottleneck, and so it was important to do so in
parallel. After writing and benchmarking several software renderers, I landed on
WebGPU. While quite bloated, WebGPU provides a fast, low level, and cross platform
rendering API designed for async programs. This allows Fishbowl to easily integrate
with both my server, which is entirely async, and the simulation, which is mostly
synchronous. While I would have liked to use a bespoke solution, GPU accelerated
rendering is far too quick to realistically consider any other option.
Try It Out
You can try out Fishbowl below, or download the CLI tool from my GitHub.
It works best on images that are roughly square and don't contain any text.
Keep in mind the web version below will be slower and generate lower quality
results than the native application, as it is being run on my home server with
limited resources.