Radix docs · Benchmark summary · October 9, 2026

Radix, Spark, and PlayCanvas
drawing the same splats

Five scans from the Radix catalog (0.5M to 10M splats), each loaded from the same file by every renderer and drawn along the same camera path at 1920×1080: with culling matched between the renderers (the headline), with each renderer's defaults, and with no culling. Apple M3 Max, 128 GB, macOS 15.4.1, Google Chrome 154.0.8037.98.

01 · Summary

With the same culling, Radix and PlayCanvas are close

The renderers' defaults draw different splats: PlayCanvas drops tiny and faint splats that Radix and Spark draw, which makes it look faster than it is at like-for-like work. So the comparison here sets Radix and Spark to cull and dilate exactly as PlayCanvas does by default (section 02). With culling matched, Radix and PlayCanvas run at similar speeds on WebGPU.

The speed of each renderer relative to Radix on WebGPU with culling matched (above 1× is faster), as the geometric mean over the 5 scenes of the ratio of mean frame times.

1.00×Radix · WebGPU
0.57×Radix · WebGL2
0.62×Spark · WebGL2
1.03×PlayCanvas · WebGPU
0.78×PlayCanvas · WebGL2
00.2 ×0.4 ×0.6 ×0.8 ×1 ×vs Radix · WebGPURadix · WebGPU: 1.00×1.00×Radix · WebGPURadix · WebGL2: 0.57×0.57×Radix · WebGL2Spark · WebGL2: 0.62×0.62×Spark · WebGL2PlayCanvas · WebGPU: 1.03×1.03×PlayCanvas · WebGPUPlayCanvas · WebGL2: 0.78×0.78×PlayCanvas · WebGL2

On WebGL2 only

Spark has no WebGPU path, so the like-for-like comparison with it is on WebGL2, where all three sort on the CPU (on a worker) and draw with WebGL. With culling matched, relative to Radix on WebGL2: Radix · WebGL2 1.00×, Spark · WebGL2 1.08×, PlayCanvas · WebGL2 1.36×.

00.2 ×0.4 ×0.6 ×0.8 ×1 ×1.2 ×1.4 ×vs Radix · WebGL2Radix · WebGL2: 1.00×1.00×Radix · WebGL2Spark · WebGL2: 1.08×1.08×Spark · WebGL2PlayCanvas · WebGL2: 1.36×1.36×PlayCanvas · WebGL2
02 · Culling

What the defaults drop, and how it was matched

Every renderer skips splats it considers invisible, but by different rules. Radix and Spark follow the reference 3DGS rasterizer: they draw every splat above a tiny alpha (0.5/255) and blur each by 0.3 px² with opacity compensation. PlayCanvas also drops splats drawn under a 1-pixel radius (σ under about 0.22 px) and, on its WebGPU path, splats whose opacity × area is under 0.75 px², with an alpha floor of 1/255 and a fixed 0.075 px² blur without compensation. On scenes with many distant or faint splats, that is a lot of work skipped, and visible detail with it: on WebGPU it removes most of the bicycle's wheel spokes.

PlayCanvas's settings read larger than that because it measures its 2D covariance with twice the focal length in pixels (viewportWidth × P[0], where the pixel focal length is half of it) and halves its quad again when drawing, so the drawn size is right but its constants are in half pixels: minPixelSize: 2 is a 1 px radius, minContribution: 3 is 0.75 px², and its 0.3 dilation is 0.075 px². That dilation is a quarter of the 0.3 px² that standard 3DGS training uses, so PlayCanvas draws splats smaller than they were trained, and it cannot be changed.

cullPlayCanvas defaultin pixelsRadix (matched)Spark (matched)
contribution (opacity × 2π √det)minContribution: 3 (WebGPU only)0.75 px²objectParams.minContribution: 0.75 (WebGPU; 0 on WebGL2)no such cull (WebGL2, where PlayCanvas has none either)
pixel sizeminPixelSize: 2√8 σ under 1 pxobjectParams.minPixelRadius: 1minPixelRadius: 1
opacityalphaClipForward: 1/2551/255params.minAlpha: 1/255minAlpha: 1/255
dilation0.3, no opacity compensation (fixed)0.075 px²blurAmount: 0, preBlurAmount: 0.075blurAmount: 0, preBlurAmount: 0.075

Three culling settings

Every scene ran with three settings: matched (PlayCanvas's default culling and dilation in all three; PlayCanvas at its defaults), defaults (each renderer as it comes), and no culling (pixel size and contribution minimums at 0 and alpha 1/255 for all, the lowest PlayCanvas allows on WebGPU; each renderer keeps its own dilation), so every renderer draws every splat. Sort keys stay at each renderer's default throughout (section 11).

configDefaultsNo cullingMatched culling
Radix · WebGPUdefaultsobjectParams.minPixelRadius: 0 objectParams.minContribution: 0 params.minAlpha: 1/255objectParams.minContribution: 0.75 objectParams.minPixelRadius: 1 objectParams.blurAmount: 0 objectParams.preBlurAmount: 0.075 params.minAlpha: 1/255
Radix · WebGL2defaultsobjectParams.minPixelRadius: 0 objectParams.minContribution: 0 params.minAlpha: 1/255objectParams.minContribution: 0 objectParams.minPixelRadius: 1 objectParams.blurAmount: 0 objectParams.preBlurAmount: 0.075 params.minAlpha: 1/255
Spark · WebGL2defaultsrenderer.minPixelRadius: 0 renderer.minAlpha: 1/255renderer.minPixelRadius: 1 renderer.minAlpha: 1/255 renderer.blurAmount: 0 renderer.preBlurAmount: 0.075
PlayCanvas · WebGPUdefaultsminPixelSize: 0 minContribution: 0 alphaClipForward: 1/255its defaults (the runs of group default)
PlayCanvas · WebGL2defaultsminPixelSize: 0 minContribution: 0 alphaClipForward: 1/255its defaults (the runs of group default)

Bicycle was trained without anti-aliasing (a 0.3 px² dilation without opacity compensation), so the defaults and no-culling runs draw it that way: Radix · WebGPU objectParams.blurAmount: 0 objectParams.preBlurAmount: 0.3; Radix · WebGL2 objectParams.blurAmount: 0 objectParams.preBlurAmount: 0.3; Spark · WebGL2 renderer.blurAmount: 0 renderer.preBlurAmount: 0.3; PlayCanvas · WebGPU antiAlias: false; PlayCanvas · WebGL2 antiAlias: false. Matched keeps PlayCanvas's 0.075 px², a constant in its shaders.

00.2 ×0.4 ×0.6 ×0.8 ×1 ×1.2 ×1.4 ×Defaultsvs Radix · WebGPU (defaults)Radix · WebGPU: 1.00×1.00×Radix · WebGPURadix · WebGL2: 0.67×0.67×Radix · WebGL2Spark · WebGL2: 0.73×0.73×Spark · WebGL2PlayCanvas · WebGPU: 1.30×1.30×PlayCanvas · WebGPUPlayCanvas · WebGL2: 0.98×0.98×PlayCanvas · WebGL2No cullingvs Radix · WebGPU (no culling)Radix · WebGPU: 1.00×1.00×Radix · WebGPURadix · WebGL2: 0.67×0.67×Radix · WebGL2Spark · WebGL2: 0.72×0.72×Spark · WebGL2PlayCanvas · WebGPU: 1.05×1.05×PlayCanvas · WebGPUPlayCanvas · WebGL2: 0.92×0.92×PlayCanvas · WebGL2Matched cullingvs Radix · WebGPU (matched culling)Radix · WebGPU: 1.00×1.00×Radix · WebGPURadix · WebGL2: 0.57×0.57×Radix · WebGL2Spark · WebGL2: 0.62×0.62×Spark · WebGL2PlayCanvas · WebGPU: 1.03×1.03×PlayCanvas · WebGPUPlayCanvas · WebGL2: 0.78×0.78×PlayCanvas · WebGL2

Speed relative to Radix on WebGPU with the same culling setting (above 1× is faster), the geometric mean over the scenes. Solid: matched culling.

03 · Per scene

Frame time per scene, culling matched

Radix · WebGPURadix · WebGL2Spark · WebGL2PlayCanvas · WebGPUPlayCanvas · WebGL2
05 ms10 ms15 msButterfly546k splats · orbitRadix · WebGPU: median of 5 rounds 3.44 ms (range 3.40–3.45)3.4 ms · 290 fpsRadix · WebGPURadix · WebGL2: median of 5 rounds 4.48 ms (range 4.46–4.49)4.5 ms · 223 fpsRadix · WebGL2Spark · WebGL2: median of 5 rounds 3.69 ms (range 3.68–3.70)3.7 ms · 271 fpsSpark · WebGL2PlayCanvas · WebGPU: median of 5 rounds 3.52 ms (range 3.49–3.58)3.5 ms · 284 fpsPlayCanvas · WebGPUPlayCanvas · WebGL2: median of 5 rounds 3.51 ms (range 3.50–3.54)3.5 ms · 285 fpsPlayCanvas · WebGL2Ceramic (cave)2.0M splats · look aroundRadix · WebGPU: median of 5 rounds 5.82 ms (range 5.79–5.85)5.8 ms · 172 fpsRadix · WebGPURadix · WebGL2: median of 5 rounds 8.74 ms (range 8.71–8.75)8.7 ms · 114 fpsRadix · WebGL2Spark · WebGL2: median of 5 rounds 7.76 ms (range 7.70–7.79)7.8 ms · 129 fpsSpark · WebGL2PlayCanvas · WebGPU: median of 5 rounds 5.55 ms (range 5.52–5.58)5.5 ms · 180 fpsPlayCanvas · WebGPUPlayCanvas · WebGL2: median of 5 rounds 6.17 ms (range 6.03–6.19)6.2 ms · 162 fpsPlayCanvas · WebGL2Sutro2.0M splats · orbitRadix · WebGPU: median of 5 rounds 3.42 ms (range 3.42–3.43)3.4 ms · 292 fpsRadix · WebGPURadix · WebGL2: median of 5 rounds 5.98 ms (range 5.96–5.99)6.0 ms · 167 fpsRadix · WebGL2Spark · WebGL2: median of 5 rounds 5.41 ms (range 5.40–5.45)5.4 ms · 185 fpsSpark · WebGL2PlayCanvas · WebGPU: median of 5 rounds 3.43 ms (range 3.42–3.45)3.4 ms · 291 fpsPlayCanvas · WebGPUPlayCanvas · WebGL2: median of 5 rounds 4.08 ms (range 4.06–4.11)4.1 ms · 245 fpsPlayCanvas · WebGL2Bicycle6.1M splats · orbitRadix · WebGPU: median of 5 rounds 4.83 ms (range 4.81–4.85)4.8 ms · 207 fpsRadix · WebGPURadix · WebGL2: median of 5 rounds 12.48 ms (range 12.46–12.56)12.5 ms · 80 fpsRadix · WebGL2Spark · WebGL2: median of 5 rounds 12.14 ms (range 12.07–12.18)12.1 ms · 82 fpsSpark · WebGL2PlayCanvas · WebGPU: median of 5 rounds 4.54 ms (range 4.47–4.61)4.5 ms · 220 fpsPlayCanvas · WebGPUPlayCanvas · WebGL2: median of 5 rounds 9.36 ms (range 9.30–9.44)9.4 ms · 107 fpsPlayCanvas · WebGL2Grace interior10.0M splats · look aroundRadix · WebGPU: median of 5 rounds 8.09 ms (range 8.06–8.12)8.1 ms · 124 fpsRadix · WebGPURadix · WebGL2: median of 5 rounds 15.13 ms (range 15.01–15.46)15.1 ms · 66 fpsRadix · WebGL2Spark · WebGL2: median of 5 rounds 16.11 ms (range 16.01–16.18)16.1 ms · 62 fpsSpark · WebGL2PlayCanvas · WebGPU: median of 5 rounds 7.55 ms (range 7.44–7.73)7.6 ms · 132 fpsPlayCanvas · WebGPUPlayCanvas · WebGL2: median of 5 rounds 11.30 ms (range 11.04–11.36)11.3 ms · 88 fpsPlayCanvas · WebGL2

Mean frame time over the whole path (still, level turn, tilted turn), the median of the rounds; whiskers: the fastest and slowest round. Lower is better.

04 · Path parts

Still, turning level, turning and tilting

The path has three parts of equal length: the start view held still, a full turn at the start elevation, and a full turn tilting up and down. Objects (butterfly, Sutro, bicycle) are orbited, ±30° in elevation in the third part; in interiors (the ceramic cave, Grace Cathedral) the camera looks around, ±45° in pitch, while walking a level circle facing out (radius a tenth of the distance to the start view's target), so distance sorts must re-sort. The still part shows a renderer's cost with nothing to re-sort; the turns add sorting, and on the CPU-sort paths, orders that arrive a few frames late. Culling matched:

Radix · WebGPURadix · WebGL2Spark · WebGL2PlayCanvas · WebGPUPlayCanvas · WebGL2
ms per framestillleveltiltedstillleveltiltedstillleveltiltedstillleveltiltedstillleveltilted
Butterfly3.43.53.43.64.94.93.53.83.73.33.63.62.63.94.0
Ceramic (cave)8.44.54.59.38.58.49.66.96.87.84.54.48.15.25.1
Sutro3.23.63.54.27.26.64.86.05.43.23.63.53.64.54.2
Bicycle4.74.75.19.213.814.411.512.212.74.44.44.98.79.410.0
Grace interior9.77.66.912.416.816.216.416.215.79.17.16.512.311.010.5

The median over rounds of each part's mean frame time. Red: slower than the fastest config of that scene and part (full red at 2.5×).

05 · Frame time distribution

Medians, tails, and stalls

Per scene, culling matched: the median frame (dot), the 95th percentile (tick) and the 99th (end of the line), the median over rounds. A stall is a frame over twice the median: on the CPU-sort paths these are usually the frames that upload a new order.

Butterfly · 546k splats

02468Radix · WebGPU: median 3.4 ms, p95 4.1 ms, p99 4.8 ms; 2 frames over twice the median (of 2160)Radix · WebGPU3.4 / 4.1 / 4.8 ms · 2 stallsRadix · WebGL2: median 3.6 ms, p95 8.1 ms, p99 8.8 ms; 294 frames over twice the median (of 1440)Radix · WebGL23.6 / 8.1 / 8.8 ms · 294 stallsSpark · WebGL2: median 3.6 ms, p95 4.6 ms, p99 5.1 ms; 0 frames over twice the median (of 2160)Spark · WebGL23.6 / 4.6 / 5.1 msPlayCanvas · WebGPU: median 3.4 ms, p95 4.2 ms, p99 4.9 ms; 0 frames over twice the median (of 2160)PlayCanvas · WebGPU3.4 / 4.2 / 4.9 msPlayCanvas · WebGL2: median 3.6 ms, p95 4.7 ms, p99 5.4 ms; 0 frames over twice the median (of 2160)PlayCanvas · WebGL23.6 / 4.7 / 5.4 ms

Ceramic (cave) · 2.0M splats

05101520Radix · WebGPU: median 5.4 ms, p95 9.2 ms, p99 9.7 ms; 1 frames over twice the median (of 1440)Radix · WebGPU5.4 / 9.2 / 9.7 ms · 1 stallsRadix · WebGL2: median 8.8 ms, p95 18.6 ms, p99 21.4 ms; 59 frames over twice the median (of 720)Radix · WebGL28.8 / 18.6 / 21.4 ms · 59 stallsSpark · WebGL2: median 7.6 ms, p95 10.6 ms, p99 11.4 ms; 1 frames over twice the median (of 720)Spark · WebGL27.6 / 10.6 / 11.4 ms · 1 stallsPlayCanvas · WebGPU: median 5.1 ms, p95 8.5 ms, p99 9.4 ms; 2 frames over twice the median (of 1440)PlayCanvas · WebGPU5.1 / 8.5 / 9.4 ms · 2 stallsPlayCanvas · WebGL2: median 6.1 ms, p95 9.1 ms, p99 10.0 ms; 3 frames over twice the median (of 1440)PlayCanvas · WebGL26.1 / 9.1 / 10.0 ms · 3 stalls

Sutro · 2.0M splats

051015Radix · WebGPU: median 3.3 ms, p95 4.2 ms, p99 4.8 ms; 2 frames over twice the median (of 2160)Radix · WebGPU3.3 / 4.2 / 4.8 ms · 2 stallsRadix · WebGL2: median 4.3 ms, p95 17.0 ms, p99 17.9 ms; 199 frames over twice the median (of 1440)Radix · WebGL24.3 / 17.0 / 17.9 ms · 199 stallsSpark · WebGL2: median 5.3 ms, p95 6.8 ms, p99 7.4 ms; 1 frames over twice the median (of 1440)Spark · WebGL25.3 / 6.8 / 7.4 ms · 1 stallsPlayCanvas · WebGPU: median 3.4 ms, p95 4.2 ms, p99 4.7 ms; 3 frames over twice the median (of 2160)PlayCanvas · WebGPU3.4 / 4.2 / 4.7 ms · 3 stallsPlayCanvas · WebGL2: median 3.8 ms, p95 5.7 ms, p99 6.5 ms; 0 frames over twice the median (of 1440)PlayCanvas · WebGL23.8 / 5.7 / 6.5 ms

Bicycle · 6.1M splats

010203040Radix · WebGPU: median 4.6 ms, p95 6.0 ms, p99 6.4 ms; 0 frames over twice the median (of 1440)Radix · WebGPU4.6 / 6.0 / 6.4 msRadix · WebGL2: median 9.0 ms, p95 37.3 ms, p99 39.5 ms; 89 frames over twice the median (of 720)Radix · WebGL29.0 / 37.3 / 39.5 ms · 89 stallsSpark · WebGL2: median 11.9 ms, p95 14.1 ms, p99 15.5 ms; 0 frames over twice the median (of 720)Spark · WebGL211.9 / 14.1 / 15.5 msPlayCanvas · WebGPU: median 4.3 ms, p95 5.8 ms, p99 6.3 ms; 0 frames over twice the median (of 1440)PlayCanvas · WebGPU4.3 / 5.8 / 6.3 msPlayCanvas · WebGL2: median 8.6 ms, p95 13.1 ms, p99 14.8 ms; 2 frames over twice the median (of 1440)PlayCanvas · WebGL28.6 / 13.1 / 14.8 ms · 2 stalls

Grace interior · 10.0M splats

0102030405060Radix · WebGPU: median 8.6 ms, p95 10.3 ms, p99 10.8 ms; 0 frames over twice the median (of 720)Radix · WebGPU8.6 / 10.3 / 10.8 msRadix · WebGL2: median 11.6 ms, p95 53.2 ms, p99 56.1 ms; 71 frames over twice the median (of 720)Radix · WebGL211.6 / 53.2 / 56.1 ms · 71 stallsSpark · WebGL2: median 15.9 ms, p95 18.3 ms, p99 19.5 ms; 1 frames over twice the median (of 720)Spark · WebGL215.9 / 18.3 / 19.5 ms · 1 stallsPlayCanvas · WebGPU: median 8.2 ms, p95 9.8 ms, p99 10.8 ms; 0 frames over twice the median (of 720)PlayCanvas · WebGPU8.2 / 9.8 / 10.8 msPlayCanvas · WebGL2: median 11.9 ms, p95 16.5 ms, p99 18.0 ms; 0 frames over twice the median (of 720)PlayCanvas · WebGL211.9 / 16.5 / 18.0 ms

Main-thread time per frame

The CPU time of the frame call alone (the renderer's update, sort dispatch and draw submission), the median over rounds, culling matched. It is small for every renderer, so the timing method's lack of CPU and GPU overlap costs little.

sceneRadix · WebGPURadix · WebGL2Spark · WebGL2PlayCanvas · WebGPUPlayCanvas · WebGL2
Butterfly0.10 ms0.24 ms0.04 ms0.14 ms0.12 ms
Ceramic (cave)0.14 ms0.38 ms0.08 ms0.18 ms0.13 ms
Sutro0.10 ms0.31 ms0.06 ms0.14 ms0.12 ms
Bicycle0.13 ms0.44 ms0.13 ms0.18 ms0.21 ms
Grace interior0.19 ms0.40 ms0.14 ms0.24 ms0.23 ms
06 · Vsync

In a vsync loop

The timed numbers above measure each frame alone. An app runs in a requestAnimationFrame loop paced by the display: one frame's CPU work overlaps the previous frame's GPU work, and a frame that misses a vsync is a stutter. Every config also ran that way, in headed Chrome with vsync on (120 Hz, 3 rounds). Frame i draws pose i of the same path and nothing waits for the GPU, so a slower renderer takes longer over the path.

Per scene, the median over rounds: frames per second, the share of frames late (over 1.5 display frames), the p99 frame interval in ms, and the timed mean frame. Green: full rate; red: below it (full red at 33% below).

Culling matched

sceneRadix · WebGPURadix · WebGL2Spark · WebGL2PlayCanvas · WebGPUPlayCanvas · WebGL2
Butterfly
546k
120 fps
0.0% late · p99 10.3
timed 3.4 ms
120 fps
0.0% late · p99 10.3
timed 4.5 ms
120 fps
0.0% late · p99 10.3
timed 3.7 ms
120 fps
0.0% late · p99 10.3
timed 3.5 ms
120 fps
0.0% late · p99 10.3
timed 3.5 ms
Ceramic (cave)
2.0M
120 fps
0.0% late · p99 10.3
timed 5.8 ms
119 fps
0.8% late · p99 10.4
timed 8.7 ms
120 fps
0.0% late · p99 10.3
timed 7.8 ms
120 fps
0.0% late · p99 10.3
timed 5.5 ms
120 fps
0.0% late · p99 10.3
timed 6.2 ms
Sutro
2.0M
120 fps
0.0% late · p99 10.3
timed 3.4 ms
120 fps
0.0% late · p99 10.3
timed 6.0 ms
120 fps
0.0% late · p99 10.3
timed 5.4 ms
120 fps
0.0% late · p99 10.4
timed 3.4 ms
120 fps
0.0% late · p99 10.3
timed 4.1 ms
Bicycle
6.1M
120 fps
0.0% late · p99 10.4
timed 4.8 ms
102 fps
9.1% late · p99 35.9
timed 12.5 ms
96 fps
11.9% late · p99 34.7
timed 12.1 ms
120 fps
0.0% late · p99 10.3
timed 4.5 ms
120 fps
0.1% late · p99 10.3
timed 9.4 ms
Grace interior
10.0M
118 fps
1.6% late · p99 16.2
timed 8.1 ms
81 fps
22.9% late · p99 50.0
timed 15.1 ms
73 fps
27.1% late · p99 58.9
timed 16.1 ms
120 fps
0.0% late · p99 10.3
timed 7.6 ms
116 fps
3.3% late · p99 16.8
timed 11.3 ms

Defaults

sceneRadix · WebGPURadix · WebGL2Spark · WebGL2PlayCanvas · WebGPUPlayCanvas · WebGL2
Butterfly
546k
120 fps
0.0% late · p99 10.3
timed 3.7 ms
120 fps
0.0% late · p99 10.4
timed 4.6 ms
120 fps
0.0% late · p99 10.3
timed 3.8 ms
120 fps
0.0% late · p99 10.3
timed 3.5 ms
120 fps
0.0% late · p99 10.3
timed 3.5 ms
Ceramic (cave)
2.0M
120 fps
0.0% late · p99 10.4
timed 6.3 ms
116 fps
2.6% late · p99 17.0
timed 9.2 ms
120 fps
0.0% late · p99 10.3
timed 7.9 ms
120 fps
0.0% late · p99 10.3
timed 5.5 ms
120 fps
0.0% late · p99 10.3
timed 6.2 ms
Sutro
2.0M
120 fps
0.0% late · p99 10.3
timed 4.5 ms
120 fps
0.0% late · p99 10.3
timed 7.2 ms
120 fps
0.0% late · p99 10.3
timed 6.4 ms
120 fps
0.0% late · p99 10.4
timed 3.4 ms
120 fps
0.0% late · p99 10.3
timed 4.1 ms
Bicycle
6.1M
117 fps
2.2% late · p99 16.7
timed 8.9 ms
96 fps
13.2% late · p99 39.3
timed 13.5 ms
91 fps
14.2% late · p99 42.2
timed 12.9 ms
120 fps
0.0% late · p99 10.3
timed 4.5 ms
120 fps
0.1% late · p99 10.3
timed 9.4 ms
Grace interior
10.0M
108 fps
11.0% late · p99 17.6
timed 9.2 ms
78 fps
26.1% late · p99 50.7
timed 15.6 ms
72 fps
29.6% late · p99 59.3
timed 16.5 ms
120 fps
0.0% late · p99 10.3
timed 7.6 ms
116 fps
3.3% late · p99 16.8
timed 11.3 ms

No culling

sceneRadix · WebGPURadix · WebGL2Spark · WebGL2PlayCanvas · WebGPUPlayCanvas · WebGL2
Butterfly
546k
120 fps
0.0% late · p99 10.3
timed 3.6 ms
120 fps
0.0% late · p99 10.3
timed 4.5 ms
120 fps
0.0% late · p99 10.3
timed 3.8 ms
120 fps
0.0% late · p99 10.3
timed 3.6 ms
120 fps
0.0% late · p99 10.3
timed 3.5 ms
Ceramic (cave)
2.0M
120 fps
0.0% late · p99 10.3
timed 6.0 ms
118 fps
1.3% late · p99 16.0
timed 8.8 ms
120 fps
0.0% late · p99 10.3
timed 7.8 ms
120 fps
0.0% late · p99 10.3
timed 5.6 ms
120 fps
0.0% late · p99 10.3
timed 6.1 ms
Sutro
2.0M
120 fps
0.0% late · p99 10.3
timed 4.3 ms
120 fps
0.0% late · p99 10.3
timed 7.0 ms
120 fps
0.0% late · p99 10.3
timed 6.3 ms
120 fps
0.0% late · p99 10.3
timed 4.1 ms
120 fps
0.0% late · p99 10.3
timed 4.6 ms
Bicycle
6.1M
118 fps
1.5% late · p99 16.5
timed 8.8 ms
98 fps
11.6% late · p99 39.0
timed 13.2 ms
91 fps
14.3% late · p99 41.7
timed 12.8 ms
120 fps
0.0% late · p99 10.4
timed 7.5 ms
120 fps
0.1% late · p99 10.3
timed 9.6 ms
Grace interior
10.0M
110 fps
9.2% late · p99 17.0
timed 9.0 ms
79 fps
24.9% late · p99 50.9
timed 15.5 ms
72 fps
28.2% late · p99 59.3
timed 16.4 ms
109 fps
9.5% late · p99 17.1
timed 9.3 ms
110 fps
9.3% late · p99 18.3
timed 12.4 ms

These are requestAnimationFrame intervals, not presented frames; Chrome's backpressure keeps the two close. The display caps the rate, so on light scenes every renderer reads 120 fps.

07 · Defaults

What you get out of the box

Each renderer with its default settings, so with different culling (section 02): PlayCanvas skips work the others do. Relative to Radix on WebGPU: Radix · WebGPU 1.00×, Radix · WebGL2 0.67×, Spark · WebGL2 0.73×, PlayCanvas · WebGPU 1.30×, PlayCanvas · WebGL2 0.98×; on WebGL2, relative to Radix on WebGL2: Radix · WebGL2 1.00×, Spark · WebGL2 1.09×, PlayCanvas · WebGL2 1.47×.

1.00×Radix · WebGPU
0.67×Radix · WebGL2
0.73×Spark · WebGL2
1.30×PlayCanvas · WebGPU
0.98×PlayCanvas · WebGL2
05 ms10 ms15 msButterfly546k splats · orbitRadix · WebGPU: median of 5 rounds 3.69 ms (range 3.65–3.73)3.7 ms · 271 fpsRadix · WebGPURadix · WebGL2: median of 5 rounds 4.57 ms (range 4.55–4.59)4.6 ms · 219 fpsRadix · WebGL2Spark · WebGL2: median of 5 rounds 3.82 ms (range 3.81–3.84)3.8 ms · 262 fpsSpark · WebGL2PlayCanvas · WebGPU: median of 5 rounds 3.52 ms (range 3.49–3.58)3.5 ms · 284 fpsPlayCanvas · WebGPUPlayCanvas · WebGL2: median of 5 rounds 3.51 ms (range 3.50–3.54)3.5 ms · 285 fpsPlayCanvas · WebGL2Ceramic (cave)2.0M splats · look aroundRadix · WebGPU: median of 5 rounds 6.28 ms (range 6.25–6.32)6.3 ms · 159 fpsRadix · WebGPURadix · WebGL2: median of 5 rounds 9.18 ms (range 9.07–9.24)9.2 ms · 109 fpsRadix · WebGL2Spark · WebGL2: median of 5 rounds 7.89 ms (range 7.84–7.91)7.9 ms · 127 fpsSpark · WebGL2PlayCanvas · WebGPU: median of 5 rounds 5.55 ms (range 5.52–5.58)5.5 ms · 180 fpsPlayCanvas · WebGPUPlayCanvas · WebGL2: median of 5 rounds 6.17 ms (range 6.03–6.19)6.2 ms · 162 fpsPlayCanvas · WebGL2Sutro2.0M splats · orbitRadix · WebGPU: median of 5 rounds 4.46 ms (range 4.42–4.47)4.5 ms · 224 fpsRadix · WebGPURadix · WebGL2: median of 5 rounds 7.22 ms (range 7.18–7.25)7.2 ms · 138 fpsRadix · WebGL2Spark · WebGL2: median of 5 rounds 6.41 ms (range 6.37–6.42)6.4 ms · 156 fpsSpark · WebGL2PlayCanvas · WebGPU: median of 5 rounds 3.43 ms (range 3.42–3.45)3.4 ms · 291 fpsPlayCanvas · WebGPUPlayCanvas · WebGL2: median of 5 rounds 4.08 ms (range 4.06–4.11)4.1 ms · 245 fpsPlayCanvas · WebGL2Bicycle6.1M splats · orbitRadix · WebGPU: median of 5 rounds 8.93 ms (range 8.90–8.96)8.9 ms · 112 fpsRadix · WebGPURadix · WebGL2: median of 5 rounds 13.50 ms (range 13.42–13.54)13.5 ms · 74 fpsRadix · WebGL2Spark · WebGL2: median of 5 rounds 12.92 ms (range 12.80–12.97)12.9 ms · 77 fpsSpark · WebGL2PlayCanvas · WebGPU: median of 5 rounds 4.54 ms (range 4.47–4.61)4.5 ms · 220 fpsPlayCanvas · WebGPUPlayCanvas · WebGL2: median of 5 rounds 9.36 ms (range 9.30–9.44)9.4 ms · 107 fpsPlayCanvas · WebGL2Grace interior10.0M splats · look aroundRadix · WebGPU: median of 5 rounds 9.21 ms (range 9.18–9.26)9.2 ms · 109 fpsRadix · WebGPURadix · WebGL2: median of 5 rounds 15.60 ms (range 15.59–15.65)15.6 ms · 64 fpsRadix · WebGL2Spark · WebGL2: median of 5 rounds 16.52 ms (range 16.38–16.64)16.5 ms · 61 fpsSpark · WebGL2PlayCanvas · WebGPU: median of 5 rounds 7.55 ms (range 7.44–7.73)7.6 ms · 132 fpsPlayCanvas · WebGPUPlayCanvas · WebGL2: median of 5 rounds 11.30 ms (range 11.04–11.36)11.3 ms · 88 fpsPlayCanvas · WebGL2

Mean frame time over the whole path (still, level turn, tilted turn), the median of the rounds; whiskers: the fastest and slowest round. Lower is better.

Radix and PlayCanvas on WebGPU, by culling

Radix with its defaults and with PlayCanvas's culling, against PlayCanvas with its defaults and without culling: how much of the difference at defaults is the culling.

02 ms4 ms6 ms8 ms10 msButterfly546k splatsRadix · WebGPU (defaults): 3.7 ms · 271 fps3.7 ms · 271 fpsRadix · WebGPU (defaults)Radix · WebGPU (matched): 3.4 ms · 290 fps3.4 ms · 290 fpsRadix · WebGPU (matched)PlayCanvas · WebGPU (defaults = matched): 3.5 ms · 284 fps3.5 ms · 284 fpsPlayCanvas · WebGPU (defaults = matched)PlayCanvas · WebGPU (no culling): 3.6 ms · 275 fps3.6 ms · 275 fpsPlayCanvas · WebGPU (no culling)Ceramic (cave)2.0M splatsRadix · WebGPU (defaults): 6.3 ms · 159 fps6.3 ms · 159 fpsRadix · WebGPU (defaults)Radix · WebGPU (matched): 5.8 ms · 172 fps5.8 ms · 172 fpsRadix · WebGPU (matched)PlayCanvas · WebGPU (defaults = matched): 5.5 ms · 180 fps5.5 ms · 180 fpsPlayCanvas · WebGPU (defaults = matched)PlayCanvas · WebGPU (no culling): 5.6 ms · 178 fps5.6 ms · 178 fpsPlayCanvas · WebGPU (no culling)Sutro2.0M splatsRadix · WebGPU (defaults): 4.5 ms · 224 fps4.5 ms · 224 fpsRadix · WebGPU (defaults)Radix · WebGPU (matched): 3.4 ms · 292 fps3.4 ms · 292 fpsRadix · WebGPU (matched)PlayCanvas · WebGPU (defaults = matched): 3.4 ms · 291 fps3.4 ms · 291 fpsPlayCanvas · WebGPU (defaults = matched)PlayCanvas · WebGPU (no culling): 4.1 ms · 244 fps4.1 ms · 244 fpsPlayCanvas · WebGPU (no culling)Bicycle6.1M splatsRadix · WebGPU (defaults): 8.9 ms · 112 fps8.9 ms · 112 fpsRadix · WebGPU (defaults)Radix · WebGPU (matched): 4.8 ms · 207 fps4.8 ms · 207 fpsRadix · WebGPU (matched)PlayCanvas · WebGPU (defaults = matched): 4.5 ms · 220 fps4.5 ms · 220 fpsPlayCanvas · WebGPU (defaults = matched)PlayCanvas · WebGPU (no culling): 7.5 ms · 134 fps7.5 ms · 134 fpsPlayCanvas · WebGPU (no culling)Grace interior10.0M splatsRadix · WebGPU (defaults): 9.2 ms · 109 fps9.2 ms · 109 fpsRadix · WebGPU (defaults)Radix · WebGPU (matched): 8.1 ms · 124 fps8.1 ms · 124 fpsRadix · WebGPU (matched)PlayCanvas · WebGPU (defaults = matched): 7.6 ms · 132 fps7.6 ms · 132 fpsPlayCanvas · WebGPU (defaults = matched)PlayCanvas · WebGPU (no culling): 9.3 ms · 108 fps9.3 ms · 108 fpsPlayCanvas · WebGPU (no culling)
08 · No culling

Every renderer drawing every splat

Pixel size and contribution minimums at 0 and alpha 1/255 for all: every splat is drawn, each with its renderer's own dilation. Relative to Radix on WebGPU: Radix · WebGPU 1.00×, Radix · WebGL2 0.67×, Spark · WebGL2 0.72×, PlayCanvas · WebGPU 1.05×, PlayCanvas · WebGL2 0.92×; on WebGL2, relative to Radix on WebGL2: Radix · WebGL2 1.00×, Spark · WebGL2 1.08×, PlayCanvas · WebGL2 1.38×.

05 ms10 ms15 msButterfly546k splats · orbitRadix · WebGPU: median of 5 rounds 3.64 ms (range 3.59–3.65)3.6 ms · 275 fpsRadix · WebGPURadix · WebGL2: median of 5 rounds 4.53 ms (range 4.52–4.56)4.5 ms · 221 fpsRadix · WebGL2Spark · WebGL2: median of 5 rounds 3.80 ms (range 3.76–3.82)3.8 ms · 263 fpsSpark · WebGL2PlayCanvas · WebGPU: median of 5 rounds 3.64 ms (range 3.63–3.68)3.6 ms · 275 fpsPlayCanvas · WebGPUPlayCanvas · WebGL2: median of 5 rounds 3.51 ms (range 3.51–3.52)3.5 ms · 285 fpsPlayCanvas · WebGL2Ceramic (cave)2.0M splats · look aroundRadix · WebGPU: median of 5 rounds 6.02 ms (range 5.99–6.04)6.0 ms · 166 fpsRadix · WebGPURadix · WebGL2: median of 5 rounds 8.82 ms (range 8.80–8.93)8.8 ms · 113 fpsRadix · WebGL2Spark · WebGL2: median of 5 rounds 7.81 ms (range 7.69–7.83)7.8 ms · 128 fpsSpark · WebGL2PlayCanvas · WebGPU: median of 5 rounds 5.63 ms (range 5.58–5.66)5.6 ms · 178 fpsPlayCanvas · WebGPUPlayCanvas · WebGL2: median of 5 rounds 6.08 ms (range 6.07–6.21)6.1 ms · 165 fpsPlayCanvas · WebGL2Sutro2.0M splats · orbitRadix · WebGPU: median of 5 rounds 4.34 ms (range 4.32–4.35)4.3 ms · 230 fpsRadix · WebGPURadix · WebGL2: median of 5 rounds 7.03 ms (range 7.01–7.06)7.0 ms · 142 fpsRadix · WebGL2Spark · WebGL2: median of 5 rounds 6.33 ms (range 6.33–6.35)6.3 ms · 158 fpsSpark · WebGL2PlayCanvas · WebGPU: median of 5 rounds 4.10 ms (range 4.06–4.15)4.1 ms · 244 fpsPlayCanvas · WebGPUPlayCanvas · WebGL2: median of 5 rounds 4.58 ms (range 4.56–4.65)4.6 ms · 219 fpsPlayCanvas · WebGL2Bicycle6.1M splats · orbitRadix · WebGPU: median of 5 rounds 8.81 ms (range 8.79–8.89)8.8 ms · 114 fpsRadix · WebGPURadix · WebGL2: median of 5 rounds 13.18 ms (range 13.11–13.19)13.2 ms · 76 fpsRadix · WebGL2Spark · WebGL2: median of 5 rounds 12.77 ms (range 12.71–12.89)12.8 ms · 78 fpsSpark · WebGL2PlayCanvas · WebGPU: median of 5 rounds 7.48 ms (range 7.44–7.53)7.5 ms · 134 fpsPlayCanvas · WebGPUPlayCanvas · WebGL2: median of 5 rounds 9.63 ms (range 9.55–9.64)9.6 ms · 104 fpsPlayCanvas · WebGL2Grace interior10.0M splats · look aroundRadix · WebGPU: median of 5 rounds 9.02 ms (range 8.99–9.04)9.0 ms · 111 fpsRadix · WebGPURadix · WebGL2: median of 5 rounds 15.54 ms (range 15.45–15.58)15.5 ms · 64 fpsRadix · WebGL2Spark · WebGL2: median of 5 rounds 16.45 ms (range 16.40–16.50)16.4 ms · 61 fpsSpark · WebGL2PlayCanvas · WebGPU: median of 5 rounds 9.25 ms (range 9.22–9.43)9.3 ms · 108 fpsPlayCanvas · WebGPUPlayCanvas · WebGL2: median of 5 rounds 12.36 ms (range 12.29–12.40)12.4 ms · 81 fpsPlayCanvas · WebGL2

Mean frame time over the whole path (still, level turn, tilted turn), the median of the rounds; whiskers: the fastest and slowest round. Lower is better.

09 · Loading

Time to the first frame

01 s2 s3 s4 s5 sButterfly546k splats, 29 MiB PLYRadix · WebGPU: 0.27 s0.27 sRadix · WebGPURadix · WebGL2: 0.18 s0.18 sRadix · WebGL2Spark · WebGL2: 0.12 s0.12 sSpark · WebGL2PlayCanvas · WebGPU: 0.31 s0.31 sPlayCanvas · WebGPUPlayCanvas · WebGL2: 0.27 s0.27 sPlayCanvas · WebGL2Ceramic (cave)2.0M splats, 107 MiB PLYRadix · WebGPU: 0.56 s0.56 sRadix · WebGPURadix · WebGL2: 0.48 s0.48 sRadix · WebGL2Spark · WebGL2: 0.34 s0.34 sSpark · WebGL2PlayCanvas · WebGPU: 1.23 s1.23 sPlayCanvas · WebGPUPlayCanvas · WebGL2: 1.28 s1.28 sPlayCanvas · WebGL2Sutro2.0M splats, 107 MiB PLYRadix · WebGPU: 0.54 s0.54 sRadix · WebGPURadix · WebGL2: 0.47 s0.47 sRadix · WebGL2Spark · WebGL2: 0.35 s0.35 sSpark · WebGL2PlayCanvas · WebGPU: 0.56 s0.56 sPlayCanvas · WebGPUPlayCanvas · WebGL2: 0.51 s0.51 sPlayCanvas · WebGL2Bicycle6.1M splats, 327 MiB PLYRadix · WebGPU: 1.35 s1.35 sRadix · WebGPURadix · WebGL2: 1.33 s1.33 sRadix · WebGL2Spark · WebGL2: 1.02 s1.02 sSpark · WebGL2PlayCanvas · WebGPU: 2.38 s2.38 sPlayCanvas · WebGPUPlayCanvas · WebGL2: 2.33 s2.33 sPlayCanvas · WebGL2Grace interior10.0M splats, 534 MiB PLYRadix · WebGPU: 2.23 s2.23 sRadix · WebGPURadix · WebGL2: 2.19 s2.19 sRadix · WebGL2Spark · WebGL2: 1.62 s1.62 sSpark · WebGL2PlayCanvas · WebGPU: 5.02 s5.02 sPlayCanvas · WebGPUPlayCanvas · WebGL2: 4.91 s4.91 sPlayCanvas · WebGL2

From the start of loading (the PLY file over local HTTP) to the end of the first frame, with default settings, the median over rounds. A renderer that sorts asynchronously may still show an empty or unsorted first frame.

10 · Thermals and repeatability

Was the laptop throttling?

Before every run the driver waited for macOS to report a nominal thermal state (ProcessInfo.thermalState): up to 90 seconds while "fair", which by itself is not throttling, and up to 5 minutes at "serious" or worse, then an idle gap. A canary (Radix on WebGPU, the butterfly, 120-frame parts) ran at the start of each round, every ten runs and at the end; when it came out more than 8% slower than its first measurement, the driver cooled down for a minute and measured it again, and measured the runs since the last good canary again. The configs ran in a different random order each round, so a slow period would spread over all of them rather than one.

+8%: cool downround 1, 12:48:12 AM: 3.60 ms (0.0%), thermal 0round 1, 12:51:32 AM: 4.50 ms (25.0%), thermal 0round 1, 12:52:40 AM: 4.10 ms (13.9%), thermal 0round 1, 12:53:49 AM: 3.50 ms (-2.8%), thermal 0round 1, 12:57:04 AM: 3.60 ms (0.0%), thermal 0round 1, 1:00:03 AM: 3.60 ms (0.0%), thermal 0round 1, 1:03:04 AM: 3.60 ms (0.0%), thermal 0round 1, 1:06:19 AM: 3.60 ms (0.0%), thermal 0round 1, 1:09:11 AM: 3.60 ms (0.0%), thermal 0round 1, 1:12:22 AM: 3.60 ms (0.0%), thermal 0round 2, 1:14:03 AM: 3.60 ms (0.0%), thermal 0round 2, 1:17:07 AM: 3.60 ms (0.0%), thermal 0round 2, 1:20:08 AM: 3.60 ms (0.0%), thermal 0round 2, 1:23:05 AM: 3.60 ms (0.0%), thermal 0round 2, 1:26:13 AM: 3.60 ms (0.0%), thermal 0round 2, 1:29:35 AM: 3.60 ms (0.0%), thermal 0round 2, 1:32:45 AM: 3.60 ms (0.0%), thermal 0round 2, 1:34:19 AM: 3.60 ms (0.0%), thermal 0round 3, 1:34:29 AM: 3.50 ms (-2.8%), thermal 0round 3, 1:37:33 AM: 3.60 ms (0.0%), thermal 0round 3, 1:40:36 AM: 3.60 ms (0.0%), thermal 0round 3, 1:43:45 AM: 3.60 ms (0.0%), thermal 0round 3, 1:46:50 AM: 3.60 ms (0.0%), thermal 0round 3, 1:50:01 AM: 3.60 ms (0.0%), thermal 0round 3, 1:53:08 AM: 3.60 ms (0.0%), thermal 0round 3, 1:54:45 AM: 3.60 ms (0.0%), thermal 0round 4, 1:54:54 AM: 3.60 ms (0.0%), thermal 0round 4, 1:58:02 AM: 3.60 ms (0.0%), thermal 0round 4, 2:01:09 AM: 3.60 ms (0.0%), thermal 0round 4, 2:04:13 AM: 3.60 ms (0.0%), thermal 0round 4, 2:07:35 AM: 3.60 ms (0.0%), thermal 0round 4, 2:10:37 AM: 3.60 ms (0.0%), thermal 0round 4, 2:13:35 AM: 3.60 ms (0.0%), thermal 0round 4, 2:15:10 AM: 3.60 ms (0.0%), thermal 0round 5, 2:15:20 AM: 3.60 ms (0.0%), thermal 0round 5, 2:18:18 AM: 3.60 ms (0.0%), thermal 0round 5, 2:21:23 AM: 3.60 ms (0.0%), thermal 0round 5, 2:24:23 AM: 3.60 ms (0.0%), thermal 0round 5, 2:27:29 AM: 3.60 ms (0.0%), thermal 0round 5, 2:30:51 AM: 3.60 ms (0.0%), thermal 0round 5, 2:34:00 AM: 3.60 ms (0.0%), thermal 0round 5, 2:35:36 AM: 3.60 ms (0.0%), thermal 03.54.04.50 min10 min20 min30 min40 min50 min60 min70 min80 min90 min100 min

Canary median frame time over the benchmark (pink: thermal state nominal; red: not). The shaded band is the 8% tolerance.

Every round

Mean frame time (ms) of each round of every config, the coefficient of variation between them (yellow over 5%), and the thermal state before each run.

cullingsceneconfiground 1round 2round 3round 4round 5CVthermal before
DefaultsButterflyRadix · WebGPU3.693.733.673.703.650.9%0 0 0 0 0
DefaultsButterflyRadix · WebGL24.594.594.574.574.550.3%0 0 0 0 0
DefaultsButterflySpark · WebGL23.813.843.823.833.810.4%0 0 0 0 0
DefaultsButterflyPlayCanvas · WebGPU3.523.583.543.503.491.0%0 0 0 0 0
DefaultsButterflyPlayCanvas · WebGL23.503.543.513.523.510.4%0 0 0 0 0
DefaultsCeramic (cave)Radix · WebGPU6.326.286.256.276.290.4%0 0 0 0 0
DefaultsCeramic (cave)Radix · WebGL29.249.189.079.159.200.7%0 0 0 0 0
DefaultsCeramic (cave)Spark · WebGL27.867.917.847.897.890.3%0 0 0 0 0
DefaultsCeramic (cave)PlayCanvas · WebGPU5.555.585.565.525.540.4%0 0 0 0 0
DefaultsCeramic (cave)PlayCanvas · WebGL26.196.176.106.036.191.1%0 0 0 0 0
DefaultsSutroRadix · WebGPU4.474.434.464.424.460.5%0 0 0 0 0
DefaultsSutroRadix · WebGL27.187.257.197.227.230.4%0 0 0 0 0
DefaultsSutroSpark · WebGL26.416.376.416.426.410.3%0 0 0 0 0
DefaultsSutroPlayCanvas · WebGPU3.453.423.433.443.430.4%0 0 0 0 0
DefaultsSutroPlayCanvas · WebGL24.084.114.084.074.060.5%0 0 0 0 0
DefaultsBicycleRadix · WebGPU8.968.928.938.958.900.3%0 0 0 0 0
DefaultsBicycleRadix · WebGL213.5413.4313.4213.5013.520.4%0 0 0 0 0
DefaultsBicycleSpark · WebGL212.8012.9212.8812.9712.930.5%0 0 0 0 0
DefaultsBicyclePlayCanvas · WebGPU4.564.544.614.514.471.2%0 0 0 0 0
DefaultsBicyclePlayCanvas · WebGL29.349.369.409.309.440.6%0 0 0 0 0
DefaultsGrace interiorRadix · WebGPU9.269.189.199.249.210.3%0 0 0 0 0
DefaultsGrace interiorRadix · WebGL215.6015.6315.5915.6515.600.2%0 0 0 0 0
DefaultsGrace interiorSpark · WebGL216.4316.6316.3816.6416.520.7%0 0 0 0 0
DefaultsGrace interiorPlayCanvas · WebGPU7.557.447.737.567.541.4%0 0 0 0 0
DefaultsGrace interiorPlayCanvas · WebGL211.3611.3011.0411.2911.311.1%0 0 0 0 0
No cullingButterflyRadix · WebGPU3.643.653.643.613.590.7%0 0 0 0 0
No cullingButterflyRadix · WebGL24.524.564.554.534.530.3%0 0 0 0 0
No cullingButterflySpark · WebGL23.803.823.793.823.760.6%0 0 0 0 0
No cullingButterflyPlayCanvas · WebGPU3.683.633.633.643.640.5%0 0 0 0 0
No cullingButterflyPlayCanvas · WebGL23.523.513.523.513.510.2%0 0 0 0 0
No cullingCeramic (cave)Radix · WebGPU5.996.026.036.046.010.3%0 0 0 0 0
No cullingCeramic (cave)Radix · WebGL28.808.938.828.828.800.6%0 0 0 0 0
No cullingCeramic (cave)Spark · WebGL27.817.697.837.817.770.7%0 0 0 0 0
No cullingCeramic (cave)PlayCanvas · WebGPU5.655.635.665.585.600.6%0 0 0 0 0
No cullingCeramic (cave)PlayCanvas · WebGL26.086.076.086.076.211.0%0 0 0 0 0
No cullingSutroRadix · WebGPU4.334.324.344.354.350.3%0 0 0 0 0
No cullingSutroRadix · WebGL27.017.067.027.037.030.3%0 0 0 0 0
No cullingSutroSpark · WebGL26.336.356.336.356.330.2%0 0 0 0 0
No cullingSutroPlayCanvas · WebGPU4.064.134.154.094.100.8%0 0 0 0 0
No cullingSutroPlayCanvas · WebGL24.584.654.564.584.560.8%0 0 0 0 0
No cullingBicycleRadix · WebGPU8.818.898.868.798.800.5%0 0 0 0 0
No cullingBicycleRadix · WebGL213.1813.1913.1113.1913.150.2%0 0 0 0 0
No cullingBicycleSpark · WebGL212.7712.7112.7512.8412.890.6%0 0 0 0 0
No cullingBicyclePlayCanvas · WebGPU7.537.477.447.507.480.4%0 0 0 0 0
No cullingBicyclePlayCanvas · WebGL29.559.619.649.639.640.4%0 0 0 0 0
No cullingGrace interiorRadix · WebGPU9.049.039.029.028.990.2%0 0 0 0 0
No cullingGrace interiorRadix · WebGL215.4715.5515.4515.5815.540.4%0 0 0 0 0
No cullingGrace interiorSpark · WebGL216.4316.4516.4016.4716.500.2%0 0 0 0 0
No cullingGrace interiorPlayCanvas · WebGPU9.229.259.439.259.221.0%0 0 0 0 0
No cullingGrace interiorPlayCanvas · WebGL212.2912.4012.2912.3712.360.4%0 0 0 0 0
Matched cullingButterflyRadix · WebGPU3.443.453.443.403.450.6%0 0 0 0 0
Matched cullingButterflyRadix · WebGL24.494.494.474.464.480.3%0 0 0 0 0
Matched cullingButterflySpark · WebGL23.693.693.703.693.680.3%0 0 0 0 0
Matched cullingCeramic (cave)Radix · WebGPU5.815.825.855.845.790.4%0 0 0 0 0
Matched cullingCeramic (cave)Radix · WebGL28.748.758.748.758.710.2%0 0 0 0 0
Matched cullingCeramic (cave)Spark · WebGL27.717.767.797.767.700.5%0 0 0 0 0
Matched cullingSutroRadix · WebGPU3.433.423.423.423.420.1%0 0 0 0 0
Matched cullingSutroRadix · WebGL25.975.965.985.995.980.2%0 0 0 0 0
Matched cullingSutroSpark · WebGL25.445.415.405.415.450.4%0 0 0 0 0
Matched cullingBicycleRadix · WebGPU4.824.854.844.814.830.3%0 0 0 0 0
Matched cullingBicycleRadix · WebGL212.4812.4912.4612.4712.560.3%0 0 0 0 0
Matched cullingBicycleSpark · WebGL212.1412.1812.1212.0712.160.4%0 0 0 0 0
Matched cullingGrace interiorRadix · WebGPU8.128.098.098.068.060.3%0 0 0 0 0
Matched cullingGrace interiorRadix · WebGL215.4615.0115.0915.1415.131.1%0 0 0 0 0
Matched cullingGrace interiorSpark · WebGL216.0116.1116.1416.1816.070.4%0 0 0 0 0
11 · Method

How it was measured

Inputs

Each scan was decoded by Radix's loader and written as a standard degree-0 3DGS PLY (float32 position, log scales, rotation, logit opacity, DC color), so all three renderers load the same bytes and draw degree-0 color only: Radix has no spherical harmonics, and Spark and PlayCanvas would otherwise evaluate the degree-3 SH of the Grace, Sutro, and bicycle files. PlayCanvas reads no SPZ, so the SPZ scans needed converting anyway. The decode quantizes slightly (SPZ sources are quantized already; the PLY sources lose some color and scale precision), identically for every renderer.

scenesplatssourcepathstart view (upright frame)
Butterfly546,112/splats/bench/butterfly.plyorbit the targetfrom (0.00, -0.02, 1.33) to (0.00, -0.02, 0.00), fov 50°
Ceramic (cave)2,000,000/splats/bench/ceramic.plylook around while walking a small circlefrom (-0.23, 0.55, -4.12) to (0.00, 0.00, 0.00), fov 50°
Sutro2,001,220/splats/bench/sutro.ply (SH3 dropped)orbit the targetfrom (0.00, 1.50, -1.20) to (0.00, 1.50, 0.00), fov 60°
Bicycle6,131,954/splats/bench/bicycle.ply (SH3 dropped)orbit the targetfrom (-3.50, 0.90, 0.00) to (0.00, -0.70, 0.00), fov 55°
Grace interior9,997,774/splats/bench/grace.ply (SH3 dropped)look around while walking a small circlefrom (-14.00, 8.00, -3.00) to (-4.00, 8.00, -3.00), fov 60°

Renderers and defaults

rendererversionsetupdefaults that matter
Radixas of the run, Oct 9, 2026createRenderer() (three.js 0.186 WebGPURenderer; ?gpu=webgpu-gl for its WebGL2 backend) and one SplatRendererWebGPU: the GPU sort every frame; WebGL2: autoSort (keys on the GPU, a worker sort, one in flight). ewa, 24-bit sort keys (float bits), blur 0.3 with opacity compensation, alpha 0.5/255, no size or contribution cull, radial sort
Spark2.3.1three.js 0.186 WebGLRenderer (antialias off), a SparkRenderer and one SplatMeshWebGL2 only. A worker sort as the camera moves; 32-bit sort keys (fixed); no level of detail for plain files; blur 0.3 with opacity compensation; alpha 0.5/255; no size cull (minPixelRadius 0) and no contribution cull; radial sort
PlayCanvas2.23.1createGraphicsDevice (WebGPU or WebGL2, antialias off), an Application with a camera and one gsplat component (unified rendering)WebGPU: GPU sort; WebGL2: CPU sort on a worker. minPixelSize 2 and minContribution 3 (GPU sort only), in units of twice the focal length (a 1 px radius at √8 σ, 0.75 px²), alpha 1/255, a 0.075 px² dilation without compensation, sort along the view direction

Sort keys

Each renderer sorts with its default key width. Radix uses 24 bits: the distance's float bits without the low 7, so splats more than about 0.0015% of their distance apart are ordered. Spark always sorts 32-bit keys (the float distance). PlayCanvas picks its width from the splat count and spreads it linearly over the range of visible distances, with more of it near the camera; its GPU sort rounds the width up to whole 8-bit radix digits.

scenesplatsRadixSparkPlayCanvas · WebGPUPlayCanvas · WebGL2
Butterfly546k243224 (17 by count)17
Ceramic (cave)2.0M243224 (19 by count)19
Sutro2.0M243224 (19 by count)19
Bicycle6.1M243224 (20 by count)20
Grace interior10.0M243224 (20 by count)20

All renderers draw at 1920×1080 at pixel ratio 1 with MSAA off, onto a black background, in headless Chrome (WebGPU on Metal; WebGL2 through ANGLE on Metal). Each config runs in a fresh tab, so nothing is shared between renderers or scenes.

Timing

Caveats