photon_tracer · C++17 compiled to WebAssembly
Each pixel below comes from render_row() in include/renderer.hpp, the same function the native renderer calls, running in one Web Worker per thread. No graphics API: rays, spheres, diffuse bounces and a sky, computed on the CPU and copied into a canvas.
Try 1920 × 1080 at 100 samples with fixed bands, then switch back to claiming rows. On a 32-thread desktop that is 1.7 s against 1.15 s. The top of the frame is sky, one miss per sample; the bottom is ground with several bounces. Fixed bands leave the sky workers idle while the ground workers finish, which is why the native renderer moved to claiming rows from a shared counter.