Add a WebGPU rendering path so Phoenix can handle very large events (HL-LHC / HGCAL scale)
@rx18-eng is already working on this.
Since Jun 25, 2026.
- Dominant language
- TypeScript
- Stars
- 88
- Forks
- 105
- Avg merge
- 16h 15m
- Merged PRs (30d)
- 34
Description
What this is about
Right now Phoenix renders everything with three.js's WebGLRenderer. I checked the code: renderer-manager.ts only ever does new WebGLRenderer(...) in one place, and there is no WebGPU anywhere in the source.
WebGL has served us well, but it has a ceiling on how many objects we can draw, and a lot of the per-object work (cuts, culling, picking) runs as CPU loops every frame.
The events we will need to show keep getting bigger:
- HL-LHC (starting around 2029) stacks ~200 pileup collisions into one event
- High Granularity Calorimeters (CMS HGCAL and similar) have on the order of millions of channels
- Heavy-ion events are very dense
So the real question is whether Phoenix can still show these in a few years?
Why now (the CERN angle)
ROOT's own event display (ROOT-EVE / REve) is already moving its renderer to WebGPU. Their engine, RenderCore, uses WebGPU compute shaders, and in their CHEP paper they show 1.2 million HGCAL hexagonal prisms at interactive frame rates on a 10-year-old GPU (GTX 970). The catch is that REve requires a C++/ROOT server running behind it.
Phoenix's whole point is zero install (just a URL, runs fully in the browser, can be hosted on GitHub Pages). If we add a WebGPU path, Phoenix could be the first event display that reaches that kind of scale with no server at all. That is a genuine differentiator, not just catching up.
The timing also lines up:
- WebGPU is now supported in all major browsers (Chrome, Firefox, Safari 26, Edge, roughly 95% of users), with WebGL2 fallback for the rest.
- Three.js already ships
WebGPURenderer. - We are already on Three.js r178, and I checked
node_modules: it already includes thethree/webgpuandthree/tslbuilds, so there is no dependency upgrade needed just to begin.
Proposal
Add a second renderer path using three.js's WebGPURenderer, selected automatically when the browser supports it, with automatic fallback to the current WebGLRenderer otherwise.
Nothing changes for users without WebGPU or for existing experiment routes.
Then progressively move the heavy per-object work (cuts, culling, picking) onto the GPU instead of running CPU loops every frame.
To keep this reviewable, I'd split it into three phases.
Phase 1: WebGPU renderer path + fallback (foundation)
- Detect WebGPU support.
- When available, construct a
WebGPURendererinstead ofWebGLRendererat the single renderer construction site (renderer-manager.ts). Otherwise continue using today'sWebGLRenderer. - Verify the materials Phoenix already uses render correctly on WebGPU. From what I found these are almost entirely standard Three.js materials (
MeshBasicMaterial,MeshPhongMaterial,LineBasicMaterial,MeshToonMaterial,PointsMaterial), which automatically convert to node materials underWebGPURenderer. - Port the small number of custom shaders to TSL (Three.js Shading Language), so one shader source works on both WebGPU and WebGL. I found only three custom shader locations:
TracksMaterial(tracks.ts)- the
ShaderMaterialinphoenix-objects.ts - the hover outline shader in
effects-manager.ts
- Push the existing
InstancedMeshCaloCells path (added in #474 / #849) beyond its current limits. - Add a benchmark so we can compare cells rendered, FPS, and draw calls before and after.
This phase needs no compute shaders. It is mostly "swap the renderer, keep the fallback, confirm everything still renders, scale the instanced path up, and measure." It also tells us exactly which WebGPU features still need work, since Three.js still marks WebGPURenderer as experimental.
Phase 2: Move cuts and culling onto the GPU
- Use TSL compute shaders to evaluate collection cuts (energy thresholds, η/φ windows) on the GPU instead of CPU loops.
- Perform frustum and occlusion culling on the GPU so off-screen and hidden objects cost essentially nothing.
This is where large object counts become much smoother because the per-frame CPU work is no longer O(n).
Phase 3: GPU picking for instanced collections
- Today selection walks the scene graph (
selection-manager.ts), which is O(n). - For large instanced collections, replace this with GPU picking by rendering object IDs into an off-screen buffer and reading back the ID under the cursor.
Hover and click remain responsive even with millions of rendered objects.
Why this is safe
- Capability detection with automatic fallback means existing users and experiment routes continue using today's renderer unchanged.
- Renderer construction happens in one place, so the change is localized rather than spread throughout the codebase.
- The custom shader surface is small (three locations), and TSL allows one shader source to target both rendering backends.
- This builds directly on the existing InstancedMesh CaloCells work (#474, #849). It is an incremental extension, not a renderer rewrite.
Out of scope
- Rewriting Phoenix around WebGPU.
- Removing WebGL (it remains the fallback renderer).
- Changing the data model, loaders, or UI panels.
References
- ROOT-EVE RenderCore (WebGPU engine, including the 1.2M HGCAL benchmark): https://www.epj-conferences.org/articles/epjconf/pdf/2024/05/epjconf_chep2024_03035.pdf
- Three.js WebGPURenderer manual: https://threejs.org/manual/en/webgpurenderer.html
- Three.js 2026 / WebGPU status write-up: https://www.utsubo.com/blog/threejs-2026-what-changed
- Existing CaloCells performance work this builds on: #474, #849
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.