FastShaders is a visual 3D graphics editor for web-based virtual reality content, built to be an accessible and convenient shader programming experience for both beginners and experienced content creators. Shaders are authored in TSL (Three.js Shading Language) — edit the node graph or the code, and changes round-trip in both directions.
Main features:
- shader's impact on performance visualization
- node-function and real-time data visualizations
- visual-effect templates
- Podest — a standalone full-screen shader viewer built for
unattended pedestal displays. Drop a shader, a model or a
.zip; it reopens itself after a reload and can run for weeks. - ShaderCarousel — the benchmark suite that measures per-node GPU cost on the target headset, so the editor's cost bar shows measured numbers rather than guesses.
- a-frame-shaderloader — the A-Frame component that runs an exported shader on any entity (a git submodule, and the source of the two scripts below).
<script src="https://cdn.jsdelivr.net/gh/Alvis1/a-frame-shaderloader@master/js/a-frame-180-a-01.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/Alvis1/a-frame-shaderloader@master/js/a-frame-shaderloader-0.6.js"></script>
<a-scene renderer="backend: webgl">
<a-sphere shader="src: myshader.js" position="0 1.6 -3"></a-sphere>
</a-scene>Those two scripts are all you need: a-frame-180-a-01.min.js bundles A-Frame 1.8.0 + Three.js r184 (WebGPU), and a-frame-shaderloader-0.6.js rewrites the module's import … from 'three/tsl' to read that bundle's single Three.js instance — so no import map and no shim are required. The loader version is not a choice: every export the app produces pins it (LOADER_FILE in src/engine/tslToShaderModule.ts), so bump this snippet whenever that moves. The exported .js also works directly with Three.js, or any bundler that resolves three/tsl. Serve the page over http(s): the loader fetches myshader.js and imports it as a blob, so opening the HTML straight from disk (file://) leaves the mesh unshaded with Failed to fetch in the console.
renderer="backend: webgl" is what makes the page enter VR: Three.js r184 picks its WebGPU backend whenever navigator.gpu exists, and that backend refuses a WebXR session outright. The attribute forces the WebGL2 path, which compiles the same TSL and can present to a headset. Drop it for a flat page if you would rather have WebGPU.
- React 18 + TypeScript + Vite
- @xyflow/react v12 — node graph
- @monaco-editor/react — code editor (Monaco bundled locally, no CDN — the app works fully offline)
- zustand v5 — state management
- three.js 0.184 (WebGPU build) — shader runtime, exclusively
three/tslbuilt-ins (including the MaterialX noise family) - @babel/parser + @babel/traverse — code-to-graph parsing
- dagre — automatic graph layout
- Tauri v2 — offline desktop builds (Windows / macOS)
npm install
npm run dev # http://localhost:5173
npm test # vitest suite
npm run build # typecheck + production buildNode 20+. The vendored A-Frame and shaderloader scripts are committed under public/js/, so a plain
clone builds; the a-frame-shaderloader submodule is only needed to change them (clone with
--recurse-submodules). Desktop builds additionally need a Rust toolchain:
npm run tauri dev / npm run tauri build. Release binaries are built by CI on version tags.
Chrome 111+ · Safari 16.4+ · Firefox 126+ (and Chromium-based Edge/Opera at the Chrome floor).
Builds target esnext with no polyfills and nothing transpiled down, so the floor is whatever the
newest feature actually used demands. That feature differs per engine, and so does the damage below it
— only Safari's is a hard break, which is exactly why the single number is not the whole story:
| Engine | Floor | Set by | What an older version does |
|---|---|---|---|
| Chrome | 111 | color-mix() (CostBar.css) |
Everything works; the benchmark drop-target loses a tint (a literal rgba() fallback runs first). |
| Safari | 16.4 | ES2022 class static blocks, shipped by monaco-editor 0.55 |
The app boots and the node editor works; the code panel fails when opened, because Monaco is a lazily-loaded chunk. |
| Firefox | 126 | the non-standard zoom property (NodePreviewCard.css) |
The app works, but every asset-browser tile renders ~1.49× and is clipped by the strip. |
A browser too old to start the app at all lands on a bilingual note in index.html rather than a blank
page. That watchdog fires only when React never mounts, so it does not catch the Safari case above —
there the editor really did start.
Beyond the app itself, the 3D preview needs WebGL2 (WebGPU is used when the browser offers a working adapter, and the preview falls back on its own when it does not).
MIT. Bundled third-party components (three.js, A-Frame, Monaco, fonts, scientific colormap data…) are credited in public/THIRD-PARTY-NOTICES.txt, which ships with every build.
Alvis Misjuns
- Email: alvis.misjuns@va.lv
- Web: alvismisjuns.lv
FastShaders is doctoral research at the Faculty of Engineering, Vidzeme University of Applied Sciences, on performance-aware shader authoring for standalone VR headsets: per-node GPU costs are measured on the target device with the bundled ShaderCarousel benchmark, so the editor can show a shader's cost against a real budget while it is being built. A paper is in preparation; until it appears, please link to this repository.
This research was supported by the project No. 1.1.1.8/1/24/I/001 VeA and ViA Doctoral Grants, co-funded by the European Union (European Regional Development Fund) and the Latvian state budget within the European Union Cohesion Policy Programme 2021–2027.
