Measuring brute force against jump flooding
This benchmark originally suggested switching to jump flooding near 500 seeds. Its two paths handle offscreen seeds differently, so that number needs another measurement. The results below show what was measured and where the comparison falls short.
The historical measurement
These September 4, 2026 results came from WebGL2 at 960×540 on an Apple M4 Max. Both paths used RGBA32F render targets. The jump-flood path cleared its initial target, splatted seeds, then ran ten ping-pong passes with nine candidate samples per pixel. The jumps were 512, 256, 128, 64, 32, 16, 8, 4, 2 and 1.
Each sample timed six executions followed by a synchronous readback from the last
target, using performance.now(), then divided the elapsed time by six.
The reported value was the median of 70 samples after 30 warm-up samples.
It includes JavaScript submission, synchronization and readback overhead. It isn't
isolated GPU execution time.
| Seeds | Brute force | Jump flood | Winner |
|---|---|---|---|
| 4 | 0.050 ms | 0.417 ms | brute, 8x |
| 16 | 0.050 ms | 0.400 ms | brute, 8x |
| 64 | 0.083 ms | 0.400 ms | brute, 4.8x |
| 128 | 0.133 ms | 0.400 ms | brute, 3x |
| 256 | 0.217 ms | 0.400 ms | brute, 1.8x |
| 512 | 0.383 ms | 0.400 ms | even |
| 1024 | 0.750 ms | 0.417 ms | flood, 1.8x |
| 2048 | 1.517 ms | 0.417 ms | flood, 3.6x |
What prevents a crossover claim
The original result suggested a crossover near 500 seeds for these implementations. An audit found that the two paths didn't always evaluate the same seed set. The table is a record of that run, and doesn't establish a reliable crossover.
- Offscreen seeds differed. Brute force kept seeds that moved outside the viewport. The point splat clipped them before jump flooding could see them. A CPU reproduction of the seed equations at four seconds with four seeds gives distances of about 0.01779 and 0.06389 respectively at the bottom-left pixel, in normalized viewport coordinates. GPU hash results can differ, but clipping an offscreen seed changes the input before ordinary jump-flood approximation error.
- Seeds can share a texel. A one-point splat overwrites an earlier seed when both land in the same pixel. Brute force retains both. Initialization needs a defined collision policy, and the comparison needs to measure its error.
- The workloads differ. Brute force recomputed moving seeds in every fragment; jump flooding computed them once per seed vertex. The benchmark also used normalized viewport distances, which stretch the metric at 960×540. Neither choice matches an aspect-correct field with precomputed seed positions.
A corrected comparison needs matching seed positions and distance units, an explicit policy for offscreen and colliding seeds, and output checks against brute force. GPU timer queries can separate GPU execution from CPU and readback costs where the browser supports them. Those changes can move the measured crossover.
What the algorithm does tell us
-
Propagation depends on the grid dimensions. Basic jump flooding uses
ceil(log2(maxdim))passes. That gives ten at 960×540 and eleven at 1920×1080. Each pass touches the whole grid, so increasing resolution also increases the pixel work. Initialization and any final distance resolve add their own costs. - Seed count still matters during initialization. The propagation pass checks a fixed number of candidates per pixel, but an N-point splat processes N vertices. Its cost can be small without being zero.
- Fewer passes restrict reach. Six passes with jumps 32, 16, 8, 4, 2 and 1 can reach at most 63 pixels along an axis. They don't cover a 64-pixel radius. A shorter schedule needs correctness checks for the intended range; fewer passes alone don't guarantee a proportional runtime reduction.
- Jump flooding is approximate. It can miss the nearest seed, especially near Voronoi vertices. Extra passes can reduce errors. Floating-point seed coordinates can preserve subpixel positions, although the output field is still sampled on a grid.
- Float targets are an implementation choice. This run used them. Integer coordinates or packed seed IDs are other representations, with their own precision and lookup costs. Segments and arcs also change initialization and candidate evaluation, so point-seed timings don't predict their crossover by a fixed multiplier.
The demo and the decision
Both panels use brute force. They show the nearest-seed field and Voronoi boundaries at two seed counts; they don't compare the speed or accuracy of jump flooding. A second scan checks every bisector of the nearest seed to find the closest cell edge. Looking only at the second-nearest seed can miss that edge.
Measure the complete implementations on the hardware and resolution they'll use. Check the output as well as the time. This historical test doesn't support a general rule to switch at 500 seeds on desktop or mobile.
Sources
If you find anything wrong, let me know at hello@andrewdetwiler.com and I'll get it updated.
One email when something new goes up. No newsletter, no schedule, nothing else.
Double opt-in, so watch for a confirmation email. Unsubscribe any time.