Demo: XRUI — Shader UI toolkit
Hey folks — sharing a shader-based UI toolkit I’ve been building purely for myself. It’s a non-commercial, experimental learning project: I had an older canvas UI, rewrote it around GPU shaders (signed-distance batch), and I keep adding and ripping things out as I go. There is no “finished” build — bugs are always in there somewhere. The main job is VR interfaces — in-world chrome that actually lives in the scene, not HTML overlays or CSS3D.
Focus is on UI as a 3D object: curved multi-layer panels you can grab in XR. Widgets can have a fake depth — purely virtual parallax from the view angle, so layers separate when you look askew. Shader effects sit on top of that (glass, holofoil, an experimental water/caustic plate, etc.).
Stack
- Three.js — WebGLRenderer, curved panel meshes, WebXR
- GPU SDF shaders — rounded rects, clips, shadows, MSDF text in one pass → offscreen RT → mesh
- Panel material — analytic plate SDF + whatever FX you hang on it
- Built-in simple tweener (per-scene property tweens) — GSAP is optional, not required for UI motion
- Meant to drop into a project as one ESM file. No extra runtime deps to wire — the host just provides Three.js
- Vite / GSAP
What’s in the demo
- VR-first spatial panel (plaque + header + content) plus a dock, WASD / orbit on desktop
- Shader FX you can live-tweak: Liquid Glass, holofoil, Water / caustics (Render Settings)
- Virtual depth / parallax from view angle — not real mesh Z on every widget
- Custom themes with live crossfade (opaque + glass, light / dark)
- Import / export of the UI tree (JSON / XML) — dump a layout, load it back
- Compose throttles: cap FPS and skip redraws on static layers so the headset isn’t baking UI every frame
- Dirty-flag invalidation — a widget marks itself (and parents) dirty; only that subtree recomposes. Culled nodes stay dirty until they show again
Try it
Desktop is the main path. I’ve been testing on Quest 2, Quest 3, and AR on an Android phone. Open Render Settings if you want to break the glass on purpose.
Any feedback welcome — tell me if it feels decent or like garbage.



