Repository: playcanvas/engine
Pull request #7653 (2025-05-07 16:45:26)

Rename Key and Node classes
* Rename `Key` to `AnimationKey`
* Rename `Node` to `AnimationNode`

The original names were too generic and prone to collision. For example:

https://developer.mozilla.org/en-US/docs/Web/API/Node

This will group `Node` with other `AnimationXYZ` classes. Note that `Key` was never public API anyway but renaming nevertheless.

I confirm I have read the [contributing guidelines](https://github.com/playcanvas/engine/blob/master/.github/CONTRIBUTING.md) and signed the [Contributor License Agreement](https://docs.google.com/a/playcanvas.com/forms/d/1Ih69zQfJG-QDLIEpHr6CsaAs6fPORNOVnMv5nuo0cjk/viewform).


willeastcott: > Is the private classes only?

`Node` is pubic, `Key` is private.

Patch:
diff --git a/examples/src/examples/graphics/integer-textures.example.mjs b/examples/src/examples/graphics/integer-textures.example.mjs
index 35ffbe587..d833675f3 100644
--- a/examples/src/examples/graphics/integer-textures.example.mjs
+++ b/examples/src/examples/graphics/integer-textures.example.mjs
@@ -127,31 +127,29 @@ const outputRenderTarget = createPixelRenderTarget(2, outputTexture);
 
 // This is shader runs the sand simulation
 // It uses integer textures to store the state of each pixel
-const sandShader = pc.createShaderFromCode(
-    device,
-    pc.RenderPassShaderQuad.quadVertexShader,
-    files['sandSimulation.frag'],
-    'SandShader',
-    { aPosition: pc.SEMANTIC_POSITION },
+const sandShader = pc.ShaderUtils.createShader(device, {
+    uniqueName: 'SandShader',
+    attributes: { aPosition: pc.SEMANTIC_POSITION },
+    vertexGLSL: pc.RenderPassShaderQuad.quadVertexShader,
+    fragmentGLSL: files['sandSimulation.frag'],
     // Note that we are changing the shader output type to 'uint'
     // This means we only have to return a single integer value from the shader,
     // whereas the default is to return a vec4. This option allows you to pass
     // an array of types to specify the output type for each color attachment.
     // Unspecified types are assumed to be 'vec4'.
-    { fragmentOutputTypes: ['uint'] }
-);
+    fragmentOutputTypes: ['uint']
+});
 
 // This shader reads the integer textures
 // and renders a visual representation of the simulation
-const outputShader = pc.createShaderFromCode(
-    device,
-    pc.RenderPassShaderQuad.quadVertexShader,
-    files['renderOutput.frag'],
-    'RenderOutputShader',
-    { aPosition: pc.SEMANTIC_POSITION }
+const outputShader = pc.ShaderUtils.createShader(device, {
+    uniqueName: 'RenderOutputShader',
+    attributes: { aPosition: pc.SEMANTIC_POSITION },
+    vertexGLSL: pc.RenderPassShaderQuad.quadVertexShader,
+    fragmentGLSL: files['renderOutput.frag']
     // For the output shader, we don't need to specify the output type,
     // as we are returning a vec4 by default.
-);
+});
 
 // Write the initial simulation state to the integer texture
 const resetData = () => {
diff --git a/scripts/posteffects/posteffect-blend.js b/scripts/posteffects/posteffect-blend.js
index ba262fe0e..ce0638807 100644
--- a/scripts/posteffects/posteffect-blend.js
+++ b/scripts/posteffects/posteffect-blend.js
@@ -27,8 +27,11 @@ function BlendEffect(graphicsDevice) {
         '}'
     ].join('\n');
 
-    this.shader = pc.createShaderFromCode(graphicsDevice, pc.PostEffect.quadVertexShader, fshader, 'BlendShader', {
-        aPosition: pc.SEMANTIC_POSITION
+    this.shader = pc.ShaderUtils.createShader(graphicsDevice, {
+        uniqueName: 'BlendShader',
+        attributes: { aPosition: pc.SEMANTIC_POSITION },
+        vertexGLSL: pc.PostEffect.quadVertexShader,
+        fragmentGLSL: fshader
     });
 
     // Uniforms
diff --git a/scripts/posteffects/posteffect-bloom.js b/scripts/posteffects/posteffect-bloom.js
index 001ed24c1..7797f7f72 100644
--- a/scripts/posteffects/posteffect-bloom.js
+++ b/scripts/posteffects/posteffect-bloom.js
@@ -137,9 +137,26 @@ function BloomEffect(graphicsDevice) {
         '}'
     ].join('\n');
 
-    this.extractShader = pc.createShaderFromCode(graphicsDevice, pc.PostEffect.quadVertexShader, extractFrag, 'BloomExtractShader', attributes);
-    this.blurShader = pc.createShaderFromCode(graphicsDevice, pc.PostEffect.quadVertexShader, gaussianBlurFrag, 'BloomBlurShader', attributes);
-    this.combineShader = pc.createShaderFromCode(graphicsDevice, pc.PostEffect.quadVertexShader, combineFrag, 'BloomCombineShader', attributes);
+    this.extractShader = pc.ShaderUtils.createShader(graphicsDevice, {
+        uniqueName: 'BloomExtractShader',
+        attributes: attributes,
+        vertexGLSL: pc.PostEffect.quadVertexShader,
+        fragmentGLSL: extractFrag
+    });
+
+    this.blurShader = pc.ShaderUtils.createShader(graphicsDevice, {
+        uniqueName: 'BloomBlurShader',
+        attributes: attributes,
+        vertexGLSL: pc.PostEffect.quadVertexShader,
+        fragmentGLSL: gaussianBlurFrag
+    });
+
+    this.combineShader = pc.ShaderUtils.createShader(graphicsDevice, {
+        uniqueName: 'BloomCombineShader',
+        attributes: attributes,
+        vertexGLSL: pc.PostEffect.quadVertexShader,
+        fragmentGLSL: combineFrag
+    });
 
     this.targets = [];
 
diff --git a/scripts/posteffects/posteffect-bokeh.js b/scripts/posteffects/posteffect-bokeh.js
index b74b4e871..c96d4cba9 100644
--- a/scripts/posteffects/posteffect-bokeh.js
+++ b/scripts/posteffects/posteffect-bokeh.js
@@ -96,8 +96,11 @@ function BokehEffect(graphicsDevice) {
         '}'
     ].join('\n');
 
-    this.shader = pc.createShaderFromCode(graphicsDevice, pc.PostEffect.quadVertexShader, fshader, 'BokehShader', {
-        aPosition: pc.SEMANTIC_POSITION
+    this.shader = pc.ShaderUtils.createShader(graphicsDevice, {
+        uniqueName: 'BokehShader',
+        attributes: { aPosition: pc.SEMANTIC_POSITION },
+        vertexGLSL: pc.PostEffect.quadVertexShader,
+        fragmentGLSL: fshader
     });
 
     // Uniforms
diff --git a/scripts/posteffects/posteffect-brightnesscontrast.js b/scripts/posteffects/posteffect-brightnesscontrast.js
index dd8b82d40..55c977c9f 100644
--- a/scripts/posteffects/posteffect-brightnesscontrast.js
+++ b/scripts/posteffects/posteffect-brightnesscontrast.js
@@ -32,8 +32,11 @@ function BrightnessContrastEffect(graphicsDevice) {
         '}'
     ].join('\n');
 
-    this.shader = pc.createShaderFromCode(graphicsDevice, pc.PostEffect.quadVertexShader, fshader, 'BrightnessContrastShader', {
-        aPosition: pc.SEMANTIC_POSITION
+    this.shader = pc.ShaderUtils.createShader(graphicsDevice, {
+        uniqueName: 'BrightnessContrastShader',
+        attributes: { aPosition: pc.SEMANTIC_POSITION },
+        vertexGLSL: pc.PostEffect.quadVertexShader,
+        fragmentGLSL: fshader
     });
 
     // Uniforms
diff --git a/scripts/posteffects/posteffect-edgedetect.js b/scripts/posteffects/posteffect-edgedetect.js
index e651d31e8..c6083fbf9 100644
--- a/scripts/posteffects/posteffect-edgedetect.js
+++ b/scripts/posteffects/posteffect-edgedetect.js
@@ -52,8 +52,11 @@ function EdgeDetectEffect(graphicsDevice) {
         '}'
     ].join('\n');
 
-    this.shader = pc.createShaderFromCode(graphicsDevice, pc.PostEffect.quadVertexShader, fshader, 'EdgeDetectShader', {
-        aPosition: pc.SEMANTIC_POSITION
+    this.shader = pc.ShaderUtils.createShader(graphicsDevice, {
+        uniqueName: 'EdgeDetectShader',
+        attributes: { aPosition: pc.SEMANTIC_POSITION },
+        vertexGLSL: pc.PostEffect.quadVertexShader,
+        fragmentGLSL: fshader
     });
 
     // Uniforms
diff --git a/scripts/posteffects/posteffect-fxaa.js b/scripts/posteffects/posteffect-fxaa.js
index 5c0cc2f6f..a9486f883 100644
--- a/scripts/posteffects/posteffect-fxaa.js
+++ b/scripts/posteffects/posteffect-fxaa.js
@@ -70,8 +70,11 @@ function FxaaEffect(graphicsDevice) {
         '}'
     ].join('\n');
 
-    this.fxaaShader = pc.createShaderFromCode(graphicsDevice, pc.PostEffect.quadVertexShader, fxaaFrag, 'FxaaShader', {
-        aPosition: pc.SEMANTIC_POSITION
+    this.fxaaShader = pc.ShaderUtils.createShader(graphicsDevice, {
+        uniqueName: 'FxaaShader',
+        attributes: { aPosition: pc.SEMANTIC_POSITION },
+        vertexGLSL: pc.PostEffect.quadVertexShader,
+        fragmentGLSL: fxaaFrag
     });
 
     // Uniforms
diff --git a/scripts/posteffects/posteffect-horizontaltiltshift.js b/scripts/posteffects/posteffect-horizontaltiltshift.js
index 6583d6930..530e52120 100644
--- a/scripts/posteffects/posteffect-horizontaltiltshift.js
+++ b/scripts/posteffects/posteffect-horizontaltiltshift.js
@@ -37,8 +37,11 @@ function HorizontalTiltShiftEffect(graphicsDevice) {
         '}'
     ].join('\n');
 
-    this.shader = pc.createShaderFromCode(graphicsDevice, pc.PostEffect.quadVertexShader, fshader, 'HorizontalTiltShiftShader', {
-        aPosition: pc.SEMANTIC_POSITION
+    this.shader = pc.ShaderUtils.createShader(graphicsDevice, {
+        uniqueName: 'HorizontalTiltShiftShader',
+        attributes: { aPosition: pc.SEMANTIC_POSITION },
+        vertexGLSL: pc.PostEffect.quadVertexShader,
+        fragmentGLSL: fshader
     });
 
     // uniforms
diff --git a/scripts/posteffects/posteffect-huesaturation.js b/scripts/posteffects/posteffect-huesaturation.js
index 350b37daa..4c7b5df80 100644
--- a/scripts/posteffects/posteffect-huesaturation.js
+++ b/scripts/posteffects/posteffect-huesaturation.js
@@ -44,8 +44,11 @@ function HueSaturationEffect(graphicsDevice) {
         '}'
     ].join('\n');
 
-    this.shader = pc.createShaderFromCode(graphicsDevice, pc.PostEffect.quadVertexShader, fshader, 'HueSaturationShader', {
-        aPosition: pc.SEMANTIC_POSITION
+    this.shader = pc.ShaderUtils.createShader(graphicsDevice, {
+        uniqueName: 'HueSaturationShader',
+        attributes: { aPosition: pc.SEMANTIC_POSITION },
+        vertexGLSL: pc.PostEffect.quadVertexShader,
+        fragmentGLSL: fshader
     });
 
     // uniforms
diff --git a/scripts/posteffects/posteffect-luminosity.js b/scripts/posteffects/posteffect-luminosity.js
index 65877b39a..27b9266f7 100644
--- a/scripts/posteffects/posteffect-luminosity.js
+++ b/scripts/posteffects/posteffect-luminosity.js
@@ -23,8 +23,11 @@ function LuminosityEffect(graphicsDevice) {
         '}'
     ].join('\n');
 
-    this.shader = pc.createShaderFromCode(graphicsDevice, pc.PostEffect.quadVertexShader, fshader, 'LuminosityShader', {
-        aPosition: pc.SEMANTIC_POSITION
+    this.shader = pc.ShaderUtils.createShader(graphicsDevice, {
+        uniqueName: 'LuminosityShader',
+        attributes: { aPosition: pc.SEMANTIC_POSITION },
+        vertexGLSL: pc.PostEffect.quadVertexShader,
+        fragmentGLSL: fshader
     });
 }
 
diff --git a/scripts/posteffects/posteffect-outline.js b/scripts/posteffects/posteffect-outline.js
index 3020a83a6..44aa1b528 100644
--- a/scripts/posteffects/posteffect-outline.js
+++ b/scripts/posteffects/posteffect-outline.js
@@ -46,8 +46,11 @@ function OutlineEffect(graphicsDevice, thickness) {
         '}'
     ].join('\n');
 
-    this.shader = pc.createShaderFromCode(graphicsDevice, pc.PostEffect.quadVertexShader, fshader, 'OutlineShader', {
-        aPosition: pc.SEMANTIC_POSITION
+    this.shader = pc.ShaderUtils.createShader(graphicsDevice, {
+        uniqueName: 'OutlineShader',
+        attributes: { aPosition: pc.SEMANTIC_POSITION },
+        vertexGLSL: pc.PostEffect.quadVertexShader,
+        fragmentGLSL: fshader
     });
 
     // Uniforms
diff --git a/scripts/posteffects/posteffect-sepia.js b/scripts/posteffects/posteffect-sepia.js
index 2e05b6e4b..eb60f4648 100644
--- a/scripts/posteffects/posteffect-sepia.js
+++ b/scripts/posteffects/posteffect-sepia.js
@@ -29,8 +29,11 @@ function SepiaEffect(graphicsDevice) {
         '}'
     ].join('\n');
 
-    this.shader = pc.createShaderFromCode(graphicsDevice, pc.PostEffect.quadVertexShader, fshader, 'SepiaShader', {
-        aPosition: pc.SEMANTIC_POSITION
+    this.shader = pc.ShaderUtils.createShader(graphicsDevice, {
+        uniqueName: 'SepiaShader',
+        attributes: { aPosition: pc.SEMANTIC_POSITION },
+        vertexGLSL: pc.PostEffect.quadVertexShader,
+        fragmentGLSL: fshader
     });
 
     // Uniforms
diff --git a/scripts/posteffects/posteffect-ssao.js b/scripts/posteffects/posteffect-ssao.js
index 2b6f20038..e14a737b7 100644
--- a/scripts/posteffects/posteffect-ssao.js
+++ b/scripts/posteffects/posteffect-ssao.js
@@ -316,9 +316,27 @@ function SSAOEffect(graphicsDevice, ssaoScript) {
         aPosition: pc.SEMANTIC_POSITION
     };
 
-    this.ssaoShader = pc.createShaderFromCode(graphicsDevice, pc.PostEffect.quadVertexShader, fSsao, 'SsaoShader', attributes);
-    this.blurShader = pc.createShaderFromCode(graphicsDevice, pc.PostEffect.quadVertexShader, fblur, 'SsaoBlurShader', attributes);
-    this.outputShader = pc.createShaderFromCode(graphicsDevice, pc.PostEffect.quadVertexShader, foutput, 'SsaoOutputShader', attributes);
+    this.ssaoShader = pc.ShaderUtils.createShader(graphicsDevice, {
+        uniqueName: 'SsaoShader',
+        attributes: attributes,
+        vertexGLSL: pc.PostEffect.quadVertexShader,
+        fragmentGLSL: fSsao
+    });
+
+    this.blurShader = pc.ShaderUtils.createShader(graphicsDevice, {
+        uniqueName: 'SsaoBlurShader',
+        attributes: attributes,
+        vertexGLSL: pc.PostEffect.quadVertexShader,
+        fragmentGLSL: fblur
+    });
+
+    this.outputShader = pc.ShaderUtils.createShader(graphicsDevice, {
+        uniqueName: 'SsaoOutputShader',
+        attributes: attributes,
+        vertexGLSL: pc.PostEffect.quadVertexShader,
+        fragmentGLSL: foutput
+    });
+
 
     // Uniforms
     this.radius = 4;
diff --git a/scripts/posteffects/posteffect-verticaltiltshift.js b/scripts/posteffects/posteffect-verticaltiltshift.js
index 203fec6d4..0d4483569 100644
--- a/scripts/posteffects/posteffect-verticaltiltshift.js
+++ b/scripts/posteffects/posteffect-verticaltiltshift.js
@@ -37,8 +37,11 @@ function VerticalTiltShiftEffect(graphicsDevice) {
         '}'
     ].join('\n');
 
-    this.shader = pc.createShaderFromCode(graphicsDevice, pc.PostEffect.quadVertexShader, fshader, 'VerticalTiltShiftShader', {
-        aPosition: pc.SEMANTIC_POSITION
+    this.shader = pc.ShaderUtils.createShader(graphicsDevice, {
+        uniqueName: 'VerticalTiltShiftShader',
+        attributes: { aPosition: pc.SEMANTIC_POSITION },
+        vertexGLSL: pc.PostEffect.quadVertexShader,
+        fragmentGLSL: fshader
     });
 
     // uniforms
diff --git a/scripts/posteffects/posteffect-vignette.js b/scripts/posteffects/posteffect-vignette.js
index e4e8863f5..144cfc3bc 100644
--- a/scripts/posteffects/posteffect-vignette.js
+++ b/scripts/posteffects/posteffect-vignette.js
@@ -27,8 +27,11 @@ function VignetteEffect(graphicsDevice) {
         '}'
     ].join('\n');
 
-    this.vignetteShader = pc.createShaderFromCode(graphicsDevice, pc.PostEffect.quadVertexShader, luminosityFrag, 'VignetteShader', {
-        aPosition: pc.SEMANTIC_POSITION
+    this.vignetteShader = pc.ShaderUtils.createShader(graphicsDevice, {
+        uniqueName: 'VignetteShader',
+        attributes: { aPosition: pc.SEMANTIC_POSITION },
+        vertexGLSL: pc.PostEffect.quadVertexShader,
+        fragmentGLSL: luminosityFrag
     });
 
     this.offset = 1;
diff --git a/src/deprecated/deprecated.js b/src/deprecated/deprecated.js
index 2cecc7ac0..2930e69f4 100644
--- a/src/deprecated/deprecated.js
+++ b/src/deprecated/deprecated.js
@@ -11,7 +11,7 @@ import {
     TEXTURETYPE_DEFAULT, TEXTURETYPE_RGBM, TEXTURETYPE_SWIZZLEGGGR
 } from '../platform/graphics/constants.js';
 import { drawQuadWithShader } from '../scene/graphics/quad-render-utils.js';
-import { shaderChunks } from '../scene/shader-lib/chunks/chunks.js';
+import { shaderChunks } from '../scene/shader-lib/chunks-glsl/chunks.js';
 import { GraphicsDevice } from '../platform/graphics/graphics-device.js';
 import { LayerComposition } from '../scene/composition/layer-composition.js';
 import { RenderTarget } from '../platform/graphics/render-target.js';
@@ -20,6 +20,8 @@ import { VertexFormat } from '../platform/graphics/vertex-format.js';
 import { BlendState } from '../platform/graphics/blend-state.js';
 import { DepthState } from '../platform/graphics/depth-state.js';
 
+import { AnimationKey, AnimationNode } from '../scene/animation/animation.js';
+import { Geometry } from '../scene/geometry/geometry.js';
 import { CylinderGeometry } from '../scene/geometry/cylinder-geometry.js';
 import { BoxGeometry } from '../scene/geometry/box-geometry.js';
 import { CapsuleGeometry } from '../scene/geometry/capsule-geometry.js';
@@ -57,7 +59,6 @@ import {
 } from '../framework/components/rigid-body/constants.js';
 import { RigidBodyComponent } from '../framework/components/rigid-body/component.js';
 import { RigidBodyComponentSystem } from '../framework/components/rigid-body/system.js';
-import { Geometry } from '../scene/geometry/geometry.js';
 import { CameraComponent } from '../framework/components/camera/component.js';
 
 // MATH
@@ -408,6 +409,9 @@ GraphicsDevice.prototype.getCullMode = function () {
 
 // SCENE
 
+export const Key = AnimationKey;
+export const Node = AnimationNode;
+
 export const LitOptions = LitShaderOptions;
 
 Object.defineProperty(Scene.prototype, 'defaultMaterial', {
diff --git a/src/extras/exporters/core-exporter.js b/src/extras/exporters/core-exporter.js
index 47ad70e15..168d1b04f 100644
--- a/src/extras/exporters/core-exporter.js
+++ b/src/extras/exporters/core-exporter.js
@@ -1,13 +1,13 @@
-import { createShaderFromCode } from '../../scene/shader-lib/utils.js';
+import { ShaderUtils } from '../../scene/shader-lib/shader-utils.js';
 import { Texture } from '../../platform/graphics/texture.js';
 import { BlendState } from '../../platform/graphics/blend-state.js';
 import { drawQuadWithShader } from '../../scene/graphics/quad-render-utils.js';
 import { RenderTarget } from '../../platform/graphics/render-target.js';
 import {
     FILTER_LINEAR, ADDRESS_CLAMP_TO_EDGE, isCompressedPixelFormat, PIXELFORMAT_RGBA8,
-    SEMANTIC_POSITION, SHADERLANGUAGE_WGSL, SHADERLANGUAGE_GLSL
+    SEMANTIC_POSITION
 } from '../../platform/graphics/constants.js';
-import { shaderChunks } from '../../scene/shader-lib/chunks/chunks.js';
+import { shaderChunks } from '../../scene/shader-lib/chunks-glsl/chunks.js';
 import { shaderChunksWGSL } from '../../scene/shader-lib/chunks-wgsl/chunks-wgsl.js';
 
 /**
@@ -102,14 +102,15 @@ class CoreExporter {
             depth: false
         });
 
-        // render to a render target using a blit shader
-        const shader = device.isWebGPU ?
-            createShaderFromCode(device, shaderChunksWGSL.fullscreenQuadVS, shaderChunksWGSL.outputTex2DPS, 'ShaderCoreExporterBlit',
-                { vertex_position: SEMANTIC_POSITION }, { shaderLanguage: SHADERLANGUAGE_WGSL }
-            ) :
-            createShaderFromCode(device, shaderChunks.fullscreenQuadVS, shaderChunks.outputTex2DPS, 'ShaderCoreExporterBlit',
-                { vertex_position: SEMANTIC_POSITION }, { shaderLanguage: SHADERLANGUAGE_GLSL }
-            );
+        const shader = ShaderUtils.createShader(device, {
+            uniqueName: 'ShaderCoreExporterBlit',
+            attributes: { vertex_position: SEMANTIC_POSITION },
+            vertexGLSL: shaderChunks.fullscreenQuadVS,
+            fragmentGLSL: shaderChunks.outputTex2DPS,
+            vertexWGSL: shaderChunksWGSL.fullscreenQuadVS,
+            fragmentWGSL: shaderChunksWGSL.outputTex2DPS
+        });
+
         device.scope.resolve('source').setValue(texture);
         device.setBlendState(BlendState.NOBLEND);
         drawQuadWithShader(device, renderTarget, shader);
diff --git a/src/extras/render-passes/render-pass-compose.js b/src/extras/render-passes/render-pass-compose.js
index 1aea57fa2..65330ead5 100644
--- a/src/extras/render-passes/render-pass-compose.js
+++ b/src/extras/render-passes/render-pass-compose.js
@@ -1,7 +1,7 @@
 import { math } from '../../core/math/math.js';
 import { Color } from '../../core/math/color.js';
 import { RenderPassShaderQuad } from '../../scene/graphics/render-pass-shader-quad.js';
-import { shaderChunks } from '../../scene/shader-lib/chunks/chunks.js';
+import { shaderChunks } from '../../scene/shader-lib/chunks-glsl/chunks.js';
 import { GAMMA_NONE, GAMMA_SRGB, gammaNames, TONEMAP_LINEAR, tonemapNames } from '../../scene/constants.js';
 
 // Contrast Adaptive Sharpening (CAS) is used to apply the sharpening. It's based on AMD's
diff --git a/src/extras/render-passes/render-pass-taa.js b/src/extras/render-passes/render-pass-taa.js
index fbac090c5..77e77445f 100644
--- a/src/extras/render-passes/render-pass-taa.js
+++ b/src/extras/render-passes/render-pass-taa.js
@@ -3,7 +3,7 @@ import {
     ADDRESS_CLAMP_TO_EDGE
 } from '../../platform/graphics/constants.js';
 import { Texture } from '../../platform/graphics/texture.js';
-import { shaderChunks } from '../../scene/shader-lib/chunks/chunks.js';
+import { shaderChunks } from '../../scene/shader-lib/chunks-glsl/chunks.js';
 import { RenderPassShaderQuad } from '../../scene/graphics/render-pass-shader-quad.js';
 import { RenderTarget } from '../../platform/graphics/render-target.js';
 import { PROJECTION_ORTHOGRAPHIC } from '../../scene/constants.js';
diff --git a/src/extras/renderers/outline-renderer.js b/src/extras/renderers/outline-renderer.js
index 43f577597..7581d0c14 100644
--- a/src/extras/renderers/outline-renderer.js
+++ b/src/extras/renderers/outline-renderer.js
@@ -5,9 +5,7 @@ import {
     ADDRESS_CLAMP_TO_EDGE, BLENDEQUATION_ADD, BLENDMODE_ONE_MINUS_SRC_ALPHA, BLENDMODE_SRC_ALPHA,
     CULLFACE_NONE,
     FILTER_LINEAR, FILTER_LINEAR_MIPMAP_LINEAR, PIXELFORMAT_SRGBA8,
-    SEMANTIC_POSITION,
-    SHADERLANGUAGE_GLSL,
-    SHADERLANGUAGE_WGSL
+    SEMANTIC_POSITION
 } from '../../platform/graphics/constants.js';
 import { DepthState } from '../../platform/graphics/depth-state.js';
 import { RenderTarget } from '../../platform/graphics/render-target.js';
@@ -17,8 +15,8 @@ import { QuadRender } from '../../scene/graphics/quad-render.js';
 import { StandardMaterialOptions } from '../../scene/materials/standard-material-options.js';
 import { StandardMaterial } from '../../scene/materials/standard-material.js';
 import { shaderChunksWGSL } from '../../scene/shader-lib/chunks-wgsl/chunks-wgsl.js';
-import { shaderChunks } from '../../scene/shader-lib/chunks/chunks.js';
-import { createShaderFromCode } from '../../scene/shader-lib/utils.js';
+import { shaderChunks } from '../../scene/shader-lib/chunks-glsl/chunks.js';
+import { ShaderUtils } from '../../scene/shader-lib/shader-utils.js';
 
 /**
  * @import { AppBase } from '../../framework/app-base.js'
@@ -117,13 +115,22 @@ class OutlineRenderer {
         this.blendState = new BlendState(true, BLENDEQUATION_ADD, BLENDMODE_SRC_ALPHA, BLENDMODE_ONE_MINUS_SRC_ALPHA);
 
         const device = this.app.graphicsDevice;
-        this.shaderExtend = createShaderFromCode(device, shaderChunks.fullscreenQuadVS, shaderOutlineExtendPS, 'OutlineExtendShader');
 
-        this.shaderBlend = device.isWebGPU ?
-            createShaderFromCode(device, shaderChunksWGSL.fullscreenQuadVS, shaderChunksWGSL.outputTex2DPS, 'OutlineBlendShader',
-                { vertex_position: SEMANTIC_POSITION }, { shaderLanguage: SHADERLANGUAGE_WGSL }) :
-            createShaderFromCode(device, shaderChunks.fullscreenQuadVS, shaderChunks.outputTex2DPS, 'OutlineBlendShader',
-                { vertex_position: SEMANTIC_POSITION }, { shaderLanguage: SHADERLANGUAGE_GLSL });
+        this.shaderExtend = ShaderUtils.createShader(device, {
+            uniqueName: 'OutlineExtendShader',
+            attributes: { vertex_position: SEMANTIC_POSITION },
+            vertexGLSL: shaderChunks.fullscreenQuadVS,
+            fragmentGLSL: shaderOutlineExtendPS
+        });
+
+        this.shaderBlend = ShaderUtils.createShader(device, {
+            uniqueName: 'OutlineBlendShader',
+            attributes: { vertex_position: SEMANTIC_POSITION },
+            vertexGLSL: shaderChunks.fullscreenQuadVS,
+            fragmentGLSL: shaderChunks.outputTex2DPS,
+            vertexWGSL: shaderChunksWGSL.fullscreenQuadVS,
+            fragmentWGSL: shaderChunksWGSL.outputTex2DPS
+        });
 
         this.quadRenderer = new QuadRender(this.shaderBlend);
 
diff --git a/src/framework/asset/asset-localized.js b/src/framework/asset/asset-localized.js
index 088957d10..d6d386615 100644
--- a/src/framework/asset/asset-localized.js
+++ b/src/framework/asset/asset-localized.js
@@ -1,4 +1,5 @@
 import { EventHandler } from '../../core/event-handler.js';
+import { I18n } from '../i18n/i18n.js';
 
 import { Asset } from './asset.js';
 
@@ -7,7 +8,7 @@ class LocalizedAsset extends EventHandler {
         super();
 
         this._app = app;
-        app.i18n.on('set:locale', this._onSetLocale, this);
+        app.i18n.on(I18n.EVENT_CHANGE, this._onSetLocale, this);
 
         this._autoLoad = false;
         this._disableLocalization = false;
@@ -223,7 +224,7 @@ class LocalizedAsset extends EventHandler {
 
     destroy() {
         this.defaultAsset = null;
-        this._app.i18n.off('set:locale', this._onSetLocale, this);
+        this._app.i18n.off(I18n.EVENT_CHANGE, this._onSetLocale, this);
         this.off();
     }
 }
diff --git a/src/framework/components/element/text-element.js b/src/framework/components/element/text-element.js
index 95df5f597..da9c5a11c 100644
--- a/src/framework/components/element/text-element.js
+++ b/src/framework/components/element/text-element.js
@@ -11,6 +11,7 @@ import { MeshInstance } from '../../../scene/mesh-instance.js';
 import { Model } from '../../../scene/model.js';
 import { Mesh } from '../../../scene/mesh.js';
 import { LocalizedAsset } from '../../asset/asset-localized.js';
+import { I18n } from '../../i18n/i18n.js';
 import { FONT_BITMAP, FONT_MSDF } from '../../font/constants.js';
 import { Markup } from './markup.js';
 
@@ -213,7 +214,7 @@ class TextElement {
         element.on('set:draworder', this._onDrawOrderChange, this);
         element.on('set:pivot', this._onPivotChange, this);
 
-        this._system.app.i18n.on('set:locale', this._onLocaleSet, this);
+        this._system.app.i18n.on(I18n.EVENT_CHANGE, this._onLocaleSet, this);
         this._system.app.i18n.on('data:add', this._onLocalizationData, this);
         this._system.app.i18n.on('data:remove', this._onLocalizationData, this);
 
@@ -240,7 +241,7 @@ class TextElement {
         this._element.off('set:draworder', this._onDrawOrderChange, this);
         this._element.off('set:pivot', this._onPivotChange, this);
 
-        this._system.app.i18n.off('set:locale', this._onLocaleSet, this);
+        this._system.app.i18n.off(I18n.EVENT_CHANGE, this._onLocaleSet, this);
         this._system.app.i18n.off('data:add', this._onLocalizationData, this);
         this._system.app.i18n.off('data:remove', this._onLocalizationData, this);
     }
diff --git a/src/framework/handlers/animation.js b/src/framework/handlers/animation.js
index a40f4164a..05c8aeb17 100644
--- a/src/framework/handlers/animation.js
+++ b/src/framework/handlers/animation.js
@@ -2,7 +2,7 @@ import { path } from '../../core/path.js';
 import { Quat } from '../../core/math/quat.js';
 import { Vec3 } from '../../core/math/vec3.js';
 import { http, Http } from '../../platform/net/http.js';
-import { Animation, Key, Node } from '../../scene/animation/animation.js';
+import { Animation, AnimationKey, AnimationNode } from '../../scene/animation/animation.js';
 import { AnimEvents } from '../anim/evaluator/anim-events.js';
 import { GlbParser } from '../parsers/glb-parser.js';
 import { ResourceHandler } from './handler.js';
@@ -89,7 +89,7 @@ class AnimationHandler extends ResourceHandler {
         anim.duration = animData.duration;
 
         for (let i = 0; i < animData.nodes.length; i++) {
-            const node = new Node();
+            const node = new AnimationNode();
 
             const n = animData.nodes[i];
             node._name = n.name;
@@ -105,7 +105,7 @@ class AnimationHandler extends ResourceHandler {
                 const rot = new Quat().setFromEulerAngles(r[0], r[1], r[2]);
                 const scl = new Vec3(s[0], s[1], s[2]);
 
-                const key = new Key(t, pos, rot, scl);
+                const key = new AnimationKey(t, pos, rot, scl);
 
                 node._keys.push(key);
             }
@@ -124,7 +124,7 @@ class AnimationHandler extends ResourceHandler {
         anim.duration = animData.duration;
 
         for (let i = 0; i < animData.nodes.length; i++) {
-            const node = new Node();
+            const node = new AnimationNode();
 
             const n = animData.nodes[i];
             node._name = n.name;
@@ -144,7 +144,7 @@ class AnimationHandler extends ResourceHandler {
                 const rot = new Quat().setFromEulerAngles(r[0], r[1], r[2]);
                 const scl = new Vec3(s[0], s[1], s[2]);
 
-                const key = new Key(t, pos, rot, scl);
+                const key = new AnimationKey(t, pos, rot, scl);
 
                 node._keys.push(key);
             }
diff --git a/src/framework/i18n/i18n.js b/src/framework/i18n/i18n.js
index 50dd6a81c..df9900e42 100644
--- a/src/framework/i18n/i18n.js
+++ b/src/framework/i18n/i18n.js
@@ -14,6 +14,17 @@ import { I18nParser } from './i18n-parser.js';
  * implementation for {@link I18n#getText} and {@link I18n#getPluralText}.
  */
 class I18n extends EventHandler {
+    /**
+     * Fired when when the locale is changed.
+     *
+     * @event
+     * @example
+     * app.i18n.on('change', (newLocale, oldLocale) => {
+     *    console.log(`Locale changed from ${oldLocale} to ${newLocale}`);
+     * });
+     */
+    static EVENT_CHANGE = 'change';
+
     /**
      * Create a new I18n instance.
      *
@@ -114,7 +125,7 @@ class I18n extends EventHandler {
         this._pluralFn = getPluralFn(this._lang);
 
         // raise event
-        this.fire('set:locale', value, old);
+        this.fire(I18n.EVENT_CHANGE, value, old);
     }
 
     /**
diff --git a/src/framework/lightmapper/lightmap-filters.js b/src/framework/lightmapper/lightmap-filters.js
index 6fbdc34c2..e620f2b73 100644
--- a/src/framework/lightmapper/lightmap-filters.js
+++ b/src/framework/lightmapper/lightmap-filters.js
@@ -1,7 +1,7 @@
-import { createShaderFromCode } from '../../scene/shader-lib/utils.js';
-import { shaderChunks } from '../../scene/shader-lib/chunks/chunks.js';
+import { ShaderUtils } from '../../scene/shader-lib/shader-utils.js';
+import { shaderChunks } from '../../scene/shader-lib/chunks-glsl/chunks.js';
 import { shaderChunksWGSL } from '../../scene/shader-lib/chunks-wgsl/chunks-wgsl.js';
-import { SEMANTIC_POSITION, SHADERLANGUAGE_GLSL, SHADERLANGUAGE_WGSL } from '../../platform/graphics/constants.js';
+import { SEMANTIC_POSITION } from '../../platform/graphics/constants.js';
 
 // size of the kernel - needs to match the constant in the shader
 const DENOISE_FILTER_SIZE = 15;
@@ -41,18 +41,21 @@ class LightmapFilters {
 
         const index = bakeHDR ? 0 : 1;
         if (!this.shaderDenoise[index]) {
-            const wgsl = this.device.isWebGPU;
-            const chunks = wgsl ? shaderChunksWGSL : shaderChunks;
-            const name = `lmBilateralDeNoise-${bakeHDR ? 'hdr' : 'rgbm'}`;
 
             const defines = new Map();
             defines.set('{MSIZE}', 15);
             if (bakeHDR) defines.set('HDR', '');
 
-            this.shaderDenoise[index] = createShaderFromCode(this.device, chunks.fullscreenQuadVS, chunks.bilateralDeNoisePS, name, { vertex_position: SEMANTIC_POSITION }, undefined, {
-                shaderLanguage: wgsl ? SHADERLANGUAGE_WGSL : SHADERLANGUAGE_GLSL,
+            this.shaderDenoise[index] = ShaderUtils.createShader(this.device, {
+                uniqueName: `lmBilateralDeNoise-${bakeHDR ? 'hdr' : 'rgbm'}`,
+                attributes: { vertex_position: SEMANTIC_POSITION },
+                vertexGLSL: shaderChunks.fullscreenQuadVS,
+                vertexWGSL: shaderChunksWGSL.fullscreenQuadVS,
+                fragmentGLSL: shaderChunks.bilateralDeNoisePS,
+                fragmentWGSL: shaderChunksWGSL.bilateralDeNoisePS,
                 fragmentDefines: defines
             });
+
             this.sigmas = new Float32Array(2);
             this.constantSigmas = this.device.scope.resolve('sigmas');
             this.constantKernel = this.device.scope.resolve('kernel[0]');
@@ -74,12 +77,14 @@ class LightmapFilters {
     getDilate(device, bakeHDR) {
         const index = bakeHDR ? 0 : 1;
         if (!this.shaderDilate[index]) {
-            const wgsl = this.device.isWebGPU;
-            const chunks = wgsl ? shaderChunksWGSL : shaderChunks;
-            const name = `lmDilate-${bakeHDR ? 'hdr' : 'rgbm'}`;
             const define = bakeHDR ? '#define HDR\n' : '';
-            this.shaderDilate[index] = createShaderFromCode(device, chunks.fullscreenQuadVS, define + chunks.dilatePS, name, { vertex_position: SEMANTIC_POSITION }, undefined, {
-                shaderLanguage: wgsl ? SHADERLANGUAGE_WGSL : SHADERLANGUAGE_GLSL
+            this.shaderDilate[index] = ShaderUtils.createShader(device, {
+                uniqueName: `lmDilate-${bakeHDR ? 'hdr' : 'rgbm'}`,
+                attributes: { vertex_position: SEMANTIC_POSITION },
+                vertexGLSL: shaderChunks.fullscreenQuadVS,
+                vertexWGSL: shaderChunksWGSL.fullscreenQuadVS,
+                fragmentGLSL: define + shaderChunks.dilatePS,
+                fragmentWGSL: define + shaderChunksWGSL.dilatePS
             });
         }
         return this.shaderDilate[index];
diff --git a/src/framework/parsers/sogs.js b/src/framework/parsers/sogs.js
index 73b25bd3e..796526dd7 100644
--- a/src/framework/parsers/sogs.js
+++ b/src/framework/parsers/sogs.js
@@ -27,7 +27,7 @@ class SogsParser {
     async loadTextures(url, callback, asset, meta) {
         const { assets } = this.app;
 
-        const subs = ['means', 'opacities', 'quats', 'scales', 'sh0', 'shN'];
+        const subs = ['means', 'quats', 'scales', 'sh0', 'shN'];
 
         const textures = {};
         const promises = [];
@@ -58,29 +58,23 @@ class SogsParser {
         // wait for all textures to complete loading
         await Promise.allSettled(promises);
 
-        // sh palette has 64 sh entries per row. use width to calculate number of bands
-        const widths = {
-            192: 1,     // 64 * 3
-            512: 2,     // 64 * 8
-            960: 3      // 64 * 15
-        };
-
         // construct the gsplat resource
         const data = new GSplatSogsData();
         data.meta = meta;
         data.numSplats = meta.means.shape[0];
-        data.shBands = widths[textures.shN?.[0]?.resource?.width] ?? 0;
         data.means_l = textures.means[0].resource;
         data.means_u = textures.means[1].resource;
-        data.opacities = textures.opacities[0].resource;
         data.quats = textures.quats[0].resource;
         data.scales = textures.scales[0].resource;
         data.sh0 = textures.sh0[0].resource;
         data.sh_centroids = textures.shN?.[0]?.resource;
-        data.sh_labels_l = textures.shN?.[1]?.resource;
-        data.sh_labels_u = textures.shN?.[2]?.resource;
+        data.sh_labels = textures.shN?.[1]?.resource;
+
+        if (asset.data?.reorder ?? true) {
+            await data.reorderData();
+        }
 
-        const resource = new GSplatResource(this.app, (asset.data?.decompress) ? data.decompress() : data, []);
+        const resource = new GSplatResource(this.app, (asset.data?.decompress) ? (await data.decompress()) : data, []);
 
         callback(null, resource);
     }
diff --git a/src/framework/xr/xr-anchors.js b/src/framework/xr/xr-anchors.js
index bcde9bbaf..b8805ec4e 100644
--- a/src/framework/xr/xr-anchors.js
+++ b/src/framework/xr/xr-anchors.js
@@ -178,7 +178,7 @@ class XrAnchors extends EventHandler {
 
     /** @private */
     _onSessionStart() {
-        const available = this.manager.session.enabledFeatures.indexOf('anchors') !== -1;
+        const available = this.manager.session.enabledFeatures?.indexOf('anchors') >= 0;
         if (!available) return;
         this._available = available;
         this.fire('available');
diff --git a/src/index.js b/src/index.js
index 236cb540b..3db17b4f0 100644
--- a/src/index.js
+++ b/src/index.js
@@ -189,7 +189,7 @@ export { StencilParameters } from './platform/graphics/stencil-parameters.js';
 export { TextureAtlas } from './scene/texture-atlas.js';
 
 // SCENE / ANIMATION
-export { Animation, Key, Node } from './scene/animation/animation.js';
+export { Animation, AnimationKey, AnimationNode } from './scene/animation/animation.js';
 export { Skeleton } from './scene/animation/skeleton.js';
 
 // SCENE / GRAPHICS
@@ -223,10 +223,10 @@ export { FogParams } from './scene/fog-params.js';
 export { RenderPassForward } from './scene/renderer/render-pass-forward.js';
 
 // SCENE / SHADER-LIB
-export { createShader, createShaderFromCode } from './scene/shader-lib/utils.js';
+export { ShaderUtils, createShader, createShaderFromCode } from './scene/shader-lib/shader-utils.js';
 export { LitShaderOptions } from './scene/shader-lib/programs/lit-shader-options.js';
 export { ProgramLibrary } from './scene/shader-lib/program-library.js';
-export { shaderChunks } from './scene/shader-lib/chunks/chunks.js';
+export { shaderChunks } from './scene/shader-lib/chunks-glsl/chunks.js';
 export { ChunkUtils } from './scene/shader-lib/chunk-utils.js';
 
 // SCENE / SKY
@@ -236,6 +236,7 @@ export { Sky } from './scene/skybox/sky.js';
 export { GSplatData } from './scene/gsplat/gsplat-data.js';
 export { GSplat } from './scene/gsplat/gsplat.js';
 export { GSplatInstance } from './scene/gsplat/gsplat-instance.js';
+export { GSplatSogsData } from './scene/gsplat/gsplat-sogs-data.js';
 
 // FRAMEWORK
 export * from './framework/constants.js';
diff --git a/src/platform/graphics/shader.js b/src/platform/graphics/shader.js
index 46161c49b..a1df8e5ea 100644
--- a/src/platform/graphics/shader.js
+++ b/src/platform/graphics/shader.js
@@ -53,7 +53,7 @@ class Shader {
     /**
      * Creates a new Shader instance.
      *
-     * Consider {@link createShaderFromCode} as a simpler and more powerful way to create
+     * Consider {@link ShaderUtils#createShader} as a simpler and more powerful way to create
      * a shader.
      *
      * @param {GraphicsDevice} graphicsDevice - The graphics device used to manage this shader.
diff --git a/src/platform/graphics/webgpu/webgpu-shader-processor-wgsl.js b/src/platform/graphics/webgpu/webgpu-shader-processor-wgsl.js
index a93be3a6a..5734bed67 100644
--- a/src/platform/graphics/webgpu/webgpu-shader-processor-wgsl.js
+++ b/src/platform/graphics/webgpu/webgpu-shader-processor-wgsl.js
@@ -71,6 +71,9 @@ const getTextureInfo = (baseType, componentType) => {
             case 'u32': finalSampleType = SAMPLETYPE_UINT; break;
             case 'i32': finalSampleType = SAMPLETYPE_INT; break;
             case 'f32': finalSampleType = SAMPLETYPE_FLOAT; break;
+
+            // custom 'uff' type for unfilterable float, allowing us to create correct bind, which is automatically generated based on the shader
+            case 'uff': finalSampleType = SAMPLETYPE_UNFILTERABLE_FLOAT; break;
         }
     }
 
diff --git a/src/scene/animation/animation.js b/src/scene/animation/animation.js
index 7548926d7..3051dbadf 100644
--- a/src/scene/animation/animation.js
+++ b/src/scene/animation/animation.js
@@ -1,4 +1,4 @@
-class Key {
+class AnimationKey {
     constructor(time, position, rotation, scale) {
         this.time = time;
         this.position = position;
@@ -8,13 +8,15 @@ class Key {
 }
 
 /**
- * A animation node has a name and contains an array of keyframes.
+ * AnimationNode represents an array of keyframes that animate the transform of a {@link GraphNode}
+ * over time. Typically, an {@link Animation} maintains a collection of AnimationNodes, one for
+ * each GraphNode in a {@link Skeleton}.
  *
  * @category Animation
  */
-class Node {
+class AnimationNode {
     /**
-     * Create a new Node instance.
+     * Create a new AnimationNode instance.
      */
     constructor() {
         this._name = '';
@@ -23,8 +25,11 @@ class Node {
 }
 
 /**
- * An animation is a sequence of keyframe arrays which map to the nodes of a skeletal hierarchy. It
- * controls how the nodes of the hierarchy are transformed over time.
+ * An Animation contains the data that defines how a {@link Skeleton} animates over time. The
+ * Animation contains an array of {@link AnimationNode}s, where each AnimationNode targets a
+ * specific {@link GraphNode} referenced by a {@link Skeleton}.
+ *
+ * An Animation can be played back by an {@link AnimationComponent}.
  *
  * @category Animation
  */
@@ -52,10 +57,10 @@ class Animation {
     }
 
     /**
-     * Gets a {@link Node} by name.
+     * Gets a {@link AnimationNode} by name.
      *
-     * @param {string} name - The name of the {@link Node}.
-     * @returns {Node} The {@link Node} with the specified name.
+     * @param {string} name - The name of the {@link AnimationNode}.
+     * @returns {AnimationNode} The {@link AnimationNode} with the specified name.
      */
     getNode(name) {
         return this._nodeDict[name];
@@ -64,7 +69,7 @@ class Animation {
     /**
      * Adds a node to the internal nodes array.
      *
-     * @param {Node} node - The node to add.
+     * @param {AnimationNode} node - The node to add.
      */
     addNode(node) {
         this._nodes.push(node);
@@ -74,11 +79,11 @@ class Animation {
     /**
      * A read-only property to get array of animation nodes.
      *
-     * @type {Node[]}
+     * @type {AnimationNode[]}
      */
     get nodes() {
         return this._nodes;
     }
 }
 
-export { Animation, Key, Node };
+export { Animation, AnimationKey, AnimationNode };
diff --git a/src/scene/graphics/quad-render.js b/src/scene/graphics/quad-render.js
index cfd78623b..0ffe0d435 100644
--- a/src/scene/graphics/quad-render.js
+++ b/src/scene/graphics/quad-render.js
@@ -5,7 +5,7 @@ import { BINDGROUP_MESH, BINDGROUP_MESH_UB, BINDGROUP_VIEW, PRIMITIVE_TRISTRIP }
 import { DebugGraphics } from '../../platform/graphics/debug-graphics.js';
 import { ShaderProcessorOptions } from '../../platform/graphics/shader-processor-options.js';
 import { UniformBuffer } from '../../platform/graphics/uniform-buffer.js';
-import { processShader } from '../shader-lib/utils.js';
+import { ShaderUtils } from '../shader-lib/shader-utils.js';
 
 /**
  * @import { Shader } from '../../platform/graphics/shader.js'
@@ -28,7 +28,12 @@ const _dynamicBindGroup = new DynamicBindGroup();
  * Example:
  *
  * ```javascript
- * const shader = pc.createShaderFromCode(app.graphicsDevice, vertexShader, fragmentShader, `MyShader`);
+ * const shader = pc.ShaderUtils.createShader(app.graphicsDevice, {
+ *     uniqueName: 'MyShader',
+ *     attributes: { aPosition: SEMANTIC_POSITION },
+ *     vertexGLSL: '// vertex shader code',
+ *     fragmentGLSL: '// fragment shader code'
+ * });
  * const quad = new QuadRender(shader);
  * quad.render();
  * quad.destroy();
@@ -64,7 +69,7 @@ class QuadRender {
 
             // add uniform buffer support to shader
             const processingOptions = new ShaderProcessorOptions();
-            this.shader = processShader(shader, processingOptions);
+            this.shader = ShaderUtils.processShader(shader, processingOptions);
 
             // uniform buffer
             const ubFormat = this.shader.meshUniformBufferFormat;
diff --git a/src/scene/graphics/render-pass-shader-quad.js b/src/scene/graphics/render-pass-shader-quad.js
index 47385930a..c414c9bbd 100644
--- a/src/scene/graphics/render-pass-shader-quad.js
+++ b/src/scene/graphics/render-pass-shader-quad.js
@@ -3,7 +3,7 @@ import { BlendState } from '../../platform/graphics/blend-state.js';
 import { CULLFACE_NONE, SEMANTIC_POSITION } from '../../platform/graphics/constants.js';
 import { DepthState } from '../../platform/graphics/depth-state.js';
 import { RenderPass } from '../../platform/graphics/render-pass.js';
-import { createShaderFromCode } from '../shader-lib/utils.js';
+import { ShaderUtils } from '../shader-lib/shader-utils.js';
 
 /**
  * @import { Shader } from '../../platform/graphics/shader.js'
@@ -109,14 +109,18 @@ class RenderPassShaderQuad extends RenderPass {
      * @returns {object} Returns the created shader.
      */
     createQuadShader(name, fs, shaderDefinitionOptions = {}) {
-        return createShaderFromCode(
-            this.device,
-            RenderPassShaderQuad.quadVertexShader,
-            fs,
-            name,
-            { aPosition: SEMANTIC_POSITION },
-            shaderDefinitionOptions
-        );
+        return ShaderUtils.createShader(this.device, {
+            uniqueName: name,
+            attributes: { aPosition: SEMANTIC_POSITION },
+            useTransformFeedback: shaderDefinitionOptions.useTransformFeedback,
+            vertexGLSL: RenderPassShaderQuad.quadVertexShader,
+            vertexIncludes: shaderDefinitionOptions.vertexIncludes,
+            vertexDefines: shaderDefinitionOptions.vertexDefines,
+            fragmentGLSL: fs,
+            fragmentIncludes: shaderDefinitionOptions.fragmentIncludes,
+            fragmentDefines: shaderDefinitionOptions.fragmentDefines,
+            fragmentOutputTypes: shaderDefinitionOptions.fragmentOutputTypes
+        });
     }
 
     execute() {
diff --git a/src/scene/graphics/reproject-texture.js b/src/scene/graphics/reproject-texture.js
index f44ce082c..d356c5c4e 100644
--- a/src/scene/graphics/reproject-texture.js
+++ b/src/scene/graphics/reproject-texture.js
@@ -4,8 +4,6 @@ import { Vec3 } from '../../core/math/vec3.js';
 import {
     FILTER_NEAREST,
     TEXTUREPROJECTION_OCTAHEDRAL, TEXTUREPROJECTION_CUBE,
-    SHADERLANGUAGE_WGSL,
-    SHADERLANGUAGE_GLSL,
     SEMANTIC_POSITION
 } from '../../platform/graphics/constants.js';
 import { DebugGraphics } from '../../platform/graphics/debug-graphics.js';
@@ -13,9 +11,9 @@ import { DeviceCache } from '../../platform/graphics/device-cache.js';
 import { RenderTarget } from '../../platform/graphics/render-target.js';
 import { Texture } from '../../platform/graphics/texture.js';
 import { ChunkUtils } from '../shader-lib/chunk-utils.js';
-import { shaderChunks } from '../shader-lib/chunks/chunks.js';
+import { shaderChunks } from '../shader-lib/chunks-glsl/chunks.js';
 import { getProgramLibrary } from '../shader-lib/get-program-library.js';
-import { createShaderFromCode } from '../shader-lib/utils.js';
+import { ShaderUtils } from '../shader-lib/shader-utils.js';
 import { BlendState } from '../../platform/graphics/blend-state.js';
 import { drawQuadWithShader } from './quad-render-utils.js';
 import { shaderChunksWGSL } from '../shader-lib/chunks-wgsl/chunks-wgsl.js';
@@ -425,23 +423,22 @@ function reprojectTexture(source, target, options = {}) {
     const numSamples = options.hasOwnProperty('numSamples') ? options.numSamples : 1024;
 
     // generate unique shader key
-    const shaderKey = `${processFunc}_${decodeFunc}_${encodeFunc}_${sourceFunc}_${targetFunc}_${numSamples}`;
+    const shaderKey = `ReprojectShader:${processFunc}_${decodeFunc}_${encodeFunc}_${sourceFunc}_${targetFunc}_${numSamples}`;
 
     const device = source.device;
 
     let shader = getProgramLibrary(device).getCachedShader(shaderKey);
     if (!shader) {
-        const defines = `
-            ${prefilterSamples ? '#define USE_SAMPLES_TEX' : ''}
-            ${source.cubemap ? '#define CUBEMAP_SOURCE' : ''}
-            #define {PROCESS_FUNC} ${processFunc}
-            #define {DECODE_FUNC} ${decodeFunc}
-            #define {ENCODE_FUNC} ${encodeFunc}
-            #define {SOURCE_FUNC} ${sourceFunc}
-            #define {TARGET_FUNC} ${targetFunc}
-            #define {NUM_SAMPLES} ${numSamples}
-            #define {NUM_SAMPLES_SQRT} ${Math.round(Math.sqrt(numSamples)).toFixed(1)}
-        `;
+        const defines = new Map();
+        if (prefilterSamples) defines.set('USE_SAMPLES_TEX', '');
+        if (source.cubemap) defines.set('CUBEMAP_SOURCE', '');
+        defines.set('{PROCESS_FUNC}', processFunc);
+        defines.set('{DECODE_FUNC}', decodeFunc);
+        defines.set('{ENCODE_FUNC}', encodeFunc);
+        defines.set('{SOURCE_FUNC}', sourceFunc);
+        defines.set('{TARGET_FUNC}', targetFunc);
+        defines.set('{NUM_SAMPLES}', numSamples);
+        defines.set('{NUM_SAMPLES_SQRT}', Math.round(Math.sqrt(numSamples)).toFixed(1));
 
         const wgsl = device.isWebGPU;
         const chunks = wgsl ? shaderChunksWGSL : shaderChunks;
@@ -449,22 +446,16 @@ function reprojectTexture(source, target, options = {}) {
         includes.set('decodePS', chunks.decodePS);
         includes.set('encodePS', chunks.encodePS);
 
-        const vert = chunks.reprojectVS;
-        const frag = chunks.reprojectPS;
-        shader = createShaderFromCode(
-            device,
-            vert,
-            `
-                ${defines}
-                ${frag}
-            `,
-            shaderKey, {
-                vertex_position: SEMANTIC_POSITION
-            }, {
-                fragmentIncludes: includes,
-                shaderLanguage: wgsl ? SHADERLANGUAGE_WGSL : SHADERLANGUAGE_GLSL
-            }
-        );
+        shader = ShaderUtils.createShader(device, {
+            uniqueName: shaderKey,
+            attributes: { vertex_position: SEMANTIC_POSITION },
+            vertexGLSL: shaderChunks.reprojectVS,
+            vertexWGSL: shaderChunksWGSL.reprojectVS,
+            fragmentGLSL: shaderChunks.reprojectPS,
+            fragmentWGSL: shaderChunksWGSL.reprojectPS,
+            fragmentIncludes: includes,
+            fragmentDefines: defines
+        });
     }
 
     DebugGraphics.pushGpuMarker(device, 'ReprojectTexture');
diff --git a/src/scene/gsplat/gsplat-data.js b/src/scene/gsplat/gsplat-data.js
index 7b92ca995..0eee795fc 100644
--- a/src/scene/gsplat/gsplat-data.js
+++ b/src/scene/gsplat/gsplat-data.js
@@ -392,9 +392,9 @@ class GSplatData {
 
         const codes = new Map();
         for (let i = 0; i < this.numSplats; i++) {
-            const ix = Math.floor((x[i] - minX) * sizeX);
-            const iy = Math.floor((y[i] - minY) * sizeY);
-            const iz = Math.floor((z[i] - minZ) * sizeZ);
+            const ix = Math.min(1023, Math.floor((x[i] - minX) * sizeX));
+            const iy = Math.min(1023, Math.floor((y[i] - minY) * sizeY));
+            const iz = Math.min(1023, Math.floor((z[i] - minZ) * sizeZ));
             const code = encodeMorton3(ix, iy, iz);
 
             const val = codes.get(code);
diff --git a/src/scene/gsplat/gsplat-material.js b/src/scene/gsplat/gsplat-material.js
index 992efd878..0dca16964 100644
--- a/src/scene/gsplat/gsplat-material.js
+++ b/src/scene/gsplat/gsplat-material.js
@@ -2,7 +2,7 @@ import { CULLFACE_NONE, SEMANTIC_ATTR13, SEMANTIC_POSITION } from '../../platfor
 import { BLEND_NONE, BLEND_PREMULTIPLIED, DITHER_NONE } from '../constants.js';
 import { ShaderMaterial } from '../materials/shader-material.js';
 import { shaderChunksWGSL } from '../shader-lib/chunks-wgsl/chunks-wgsl.js';
-import { shaderChunks } from '../shader-lib/chunks/chunks.js';
+import { shaderChunks } from '../shader-lib/chunks-glsl/chunks.js';
 
 /**
  * @typedef {object} SplatMaterialOptions - The options.
diff --git a/src/scene/gsplat/gsplat-sogs-data.js b/src/scene/gsplat/gsplat-sogs-data.js
index bd4c1c3bc..5fd5092b4 100644
--- a/src/scene/gsplat/gsplat-sogs-data.js
+++ b/src/scene/gsplat/gsplat-sogs-data.js
@@ -2,21 +2,55 @@ import { Quat } from '../../core/math/quat.js';
 import { Vec3 } from '../../core/math/vec3.js';
 import { Vec4 } from '../../core/math/vec4.js';
 import { GSplatData } from './gsplat-data.js';
+import { BlendState } from '../../platform/graphics/blend-state.js';
+import { DepthState } from '../../platform/graphics/depth-state.js';
+import { RenderTarget } from '../../platform/graphics/render-target.js';
+import { Texture } from '../../platform/graphics/texture.js';
+import { CULLFACE_NONE, PIXELFORMAT_R32U, PIXELFORMAT_RGBA8, SEMANTIC_POSITION } from '../../platform/graphics/constants.js';
+import { drawQuadWithShader } from '../../scene/graphics/quad-render-utils.js';
+import { createShaderFromCode } from '../shader-lib/shader-utils.js';
 
-let offscreen = null;
-let ctx = null;
+const SH_C0 = 0.28209479177387814;
 
-const readImageData = (imageBitmap) => {
-    if (!offscreen || offscreen.width !== imageBitmap.width || offscreen.height !== imageBitmap.height) {
-        offscreen = new OffscreenCanvas(imageBitmap.width, imageBitmap.height);
-        ctx = offscreen.getContext('2d');
-        ctx.globalCompositeOperation = 'copy';
+const reorderVS = /* glsl */`
+    attribute vec2 vertex_position;
+    void main(void) {
+        gl_Position = vec4(vertex_position, 0.0, 1.0);
     }
-    ctx.drawImage(imageBitmap, 0, 0);
-    return ctx.getImageData(0, 0, imageBitmap.width, imageBitmap.height).data;
+`;
+
+const reorderFS = /* glsl */`
+    uniform usampler2D orderTexture;
+    uniform sampler2D sourceTexture;
+    uniform highp uint numSplats;
+
+    void main(void) {
+        uint w = uint(textureSize(sourceTexture, 0).x);
+        uint idx = uint(gl_FragCoord.x) + uint(gl_FragCoord.y) * w;
+        if (idx >= numSplats) discard;
+
+        // fetch the source index and calculate source uv
+        uint sidx = texelFetch(orderTexture, ivec2(gl_FragCoord.xy), 0).x;
+        uvec2 suv = uvec2(sidx % w, sidx / w);
+
+        // sample the source texture
+        gl_FragColor = texelFetch(sourceTexture, ivec2(suv), 0);
+    }
+`;
+
+const readImageDataAsync = (texture) => {
+    return texture.read(0, 0, texture.width, texture.height, {
+        mipLevel: 0,
+        face: 0,
+        immediate: true
+    });
 };
 
-const SH_C0 = 0.28209479177387814;
+const resolve = (scope, values) => {
+    for (const key in values) {
+        scope.resolve(key).setValue(values[key]);
+    }
+};
 
 class GSplatSogsIterator {
     constructor(data, p, r, s, c, sh) {
@@ -25,16 +59,16 @@ class GSplatSogsIterator {
 
         // extract means for centers
         const { meta } = data;
-        const { means, quats, scales, opacities, sh0, shN } = meta;
-        const means_l_data = p && readImageData(data.means_l._levels[0]);
-        const means_u_data = p && readImageData(data.means_u._levels[0]);
-        const quats_data = r && readImageData(data.quats._levels[0]);
-        const scales_data = s && readImageData(data.scales._levels[0]);
-        const opacities_data = c && readImageData(data.opacities._levels[0]);
-        const sh0_data = c && readImageData(data.sh0._levels[0]);
-        const sh_labels_l_data = sh && readImageData(data.sh_labels_l._levels[0]);
-        const sh_labels_u_data = sh && readImageData(data.sh_labels_u._levels[0]);
-        const sh_centroids_data = sh && readImageData(data.sh_centroids._levels[0]);
+        const { means, scales, sh0, shN } = meta;
+        const means_l_data = p && data.means_l._levels[0];
+        const means_u_data = p && data.means_u._levels[0];
+        const quats_data = r && data.quats._levels[0];
+        const scales_data = s && data.scales._levels[0];
+        const sh0_data = c && data.sh0._levels[0];
+        const sh_labels_data = sh && data.sh_labels._levels[0];
+        const sh_centroids_data = sh && data.sh_centroids._levels[0];
+
+        const norm = 2.0 / Math.sqrt(2.0);
 
         this.read = (i) => {
             if (p) {
@@ -48,11 +82,18 @@ class GSplatSogsIterator {
             }
 
             if (r) {
-                const qx = lerp(quats.mins[0], quats.maxs[0], quats_data[i * 4 + 0] / 255);
-                const qy = lerp(quats.mins[1], quats.maxs[1], quats_data[i * 4 + 1] / 255);
-                const qz = lerp(quats.mins[2], quats.maxs[2], quats_data[i * 4 + 2] / 255);
-                const qw = Math.sqrt(Math.max(0, 1 - (qx * qx + qy * qy + qz * qz)));
-                r.set(qy, qz, qw, qx);
+                const a = (quats_data[i * 4 + 0] / 255 - 0.5) * norm;
+                const b = (quats_data[i * 4 + 1] / 255 - 0.5) * norm;
+                const c = (quats_data[i * 4 + 2] / 255 - 0.5) * norm;
+                const d = Math.sqrt(Math.max(0, 1 - (a * a + b * b + c * c)));
+                const mode = quats_data[i * 4 + 3] - 252;
+
+                switch (mode) {
+                    case 0: r.set(a, b, c, d); break;
+                    case 1: r.set(d, b, c, a); break;
+                    case 2: r.set(b, d, c, a); break;
+                    case 3: r.set(b, c, d, a); break;
+                }
             }
 
             if (s) {
@@ -66,18 +107,18 @@ class GSplatSogsIterator {
                 const r = lerp(sh0.mins[0], sh0.maxs[0], sh0_data[i * 4 + 0] / 255);
                 const g = lerp(sh0.mins[1], sh0.maxs[1], sh0_data[i * 4 + 1] / 255);
                 const b = lerp(sh0.mins[2], sh0.maxs[2], sh0_data[i * 4 + 2] / 255);
-                const a = lerp(opacities.mins[0], opacities.maxs[0], opacities_data[i * 4 + 0] / 255);
+                const a = lerp(sh0.mins[3], sh0.maxs[3], sh0_data[i * 4 + 3] / 255);
 
                 c.set(
                     0.5 + r * SH_C0,
                     0.5 + g * SH_C0,
                     0.5 + b * SH_C0,
-                    1.0 / (1.0 + Math.exp(a * -1.0))
+                    1.0 / (1.0 + Math.exp(-a))
                 );
             }
 
             if (sh) {
-                const n = sh_labels_l_data[i * 4 + 0] + (sh_labels_u_data[i * 4 + 0] << 8);
+                const n = sh_labels_data[i * 4 + 0] + (sh_labels_data[i * 4 + 1] << 8);
                 const u = (n % 64) * 15;
                 const v = Math.floor(n / 64);
 
@@ -96,8 +137,6 @@ class GSplatSogsData {
 
     numSplats;
 
-    shBands;
-
     means_l;
 
     means_u;
@@ -106,15 +145,14 @@ class GSplatSogsData {
 
     scales;
 
-    opacities;
-
     sh0;
 
     sh_centroids;
 
-    sh_labels_l;
+    sh_labels;
 
-    sh_labels_u;
+    // if data is reordered at load, this texture stores the reorder indices.
+    orderTexture;
 
     createIter(p, r, s, c, sh) {
         return new GSplatSogsIterator(this, p, r, s, c, sh);
@@ -141,9 +179,10 @@ class GSplatSogsData {
     getCenters(result) {
         const p = new Vec3();
         const iter = this.createIter(p);
+        const order = this.orderTexture?._levels[0];
 
         for (let i = 0; i < this.numSplats; i++) {
-            iter.read(i);
+            iter.read(order ? order[i] : i);
 
             result[i * 3 + 0] = p.x;
             result[i * 3 + 1] = p.y;
@@ -159,7 +198,17 @@ class GSplatSogsData {
         return true;
     }
 
-    decompress() {
+    get shBands() {
+        // sh palette has 64 sh entries per row. use width to calculate number of bands
+        const widths = {
+            192: 1,     // 64 * 3
+            512: 2,     // 64 * 8
+            960: 3      // 64 * 15
+        };
+        return widths[this.sh_centroids?.resource?.width] ?? 0;
+    }
+
+    async decompress() {
         const members = [
             'x', 'y', 'z',
             'f_dc_0', 'f_dc_1', 'f_dc_2', 'opacity',
@@ -169,6 +218,16 @@ class GSplatSogsData {
 
         const { shBands } = this;
 
+        // copy back gpu texture data so cpu iterator has access to it
+        const { means_l, means_u, quats, scales, sh0, sh_labels, sh_centroids } = this;
+        means_l._levels[0] = await readImageDataAsync(means_l);
+        means_u._levels[0] = await readImageDataAsync(means_u);
+        quats._levels[0] = await readImageDataAsync(quats);
+        scales._levels[0] = await readImageDataAsync(scales);
+        sh0._levels[0] = await readImageDataAsync(sh0);
+        sh_labels._levels[0] = await readImageDataAsync(sh_labels);
+        sh_centroids._levels[0] = await readImageDataAsync(sh_centroids);
+
         // allocate spherical harmonics data
         if (shBands > 0) {
             const shMembers = [];
@@ -234,6 +293,123 @@ class GSplatSogsData {
             })
         }]);
     }
+
+    // reorder the sogs texture data in gpu memory given the ordering encoded in texture data
+    reorderGpuMemory() {
+        const { orderTexture, numSplats } = this;
+        const { device, height, width } = orderTexture;
+        const { scope } = device;
+
+        const shader = createShaderFromCode(device, reorderVS, reorderFS, 'reorderShader', {
+            vertex_position: SEMANTIC_POSITION
+        });
+
+        let targetTexture = new Texture(device, {
+            width: width,
+            height: height,
+            format: PIXELFORMAT_RGBA8,
+            mipmaps: false
+        });
+
+        const members = ['means_l', 'means_u', 'quats', 'scales', 'sh0', 'sh_labels'];
+
+        device.setBlendState(BlendState.NOBLEND);
+        device.setCullMode(CULLFACE_NONE);
+        device.setDepthState(DepthState.NODEPTH);
+
+        members.forEach((member) => {
+            const sourceTexture = this[member];
+
+            const renderTarget = new RenderTarget({
+                colorBuffer: targetTexture,
+                depth: false,
+                mipLevel: 0
+            });
+
+            resolve(scope, {
+                orderTexture,
+                sourceTexture,
+                numSplats
+            });
+
+            drawQuadWithShader(device, renderTarget, shader);
+
+            this[member] = targetTexture;
+            targetTexture.name = sourceTexture.name;
+            targetTexture._levels = sourceTexture._levels;
+            sourceTexture._levels = [];
+            targetTexture = sourceTexture;
+
+            renderTarget.destroy();
+        });
+
+        targetTexture.destroy();
+        shader.destroy();
+    }
+
+    // construct an array containing the Morton order of the splats
+    // returns an array of 32-bit unsigned integers
+    calcMortonOrder() {
+        // https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/
+        const encodeMorton3 = (x, y, z) => {
+            const Part1By2 = (x) => {
+                x &= 0x000003ff;
+                x = (x ^ (x << 16)) & 0xff0000ff;
+                x = (x ^ (x <<  8)) & 0x0300f00f;
+                x = (x ^ (x <<  4)) & 0x030c30c3;
+                x = (x ^ (x <<  2)) & 0x09249249;
+                return x;
+            };
+
+            return (Part1By2(z) << 2) + (Part1By2(y) << 1) + Part1By2(x);
+        };
+
+        const { means_l, means_u } = this;
+        const means_l_data = means_l._levels[0];
+        const means_u_data = means_u._levels[0];
+        const codes = new BigUint64Array(this.numSplats);
+
+        // generate Morton codes for each splat based on the means directly (i.e. the log-space coordinates)
+        for (let i = 0; i < this.numSplats; ++i) {
+            const ix = (means_u_data[i * 4 + 0] << 2) | (means_l_data[i * 4 + 0] >>> 6);
+            const iy = (means_u_data[i * 4 + 1] << 2) | (means_l_data[i * 4 + 1] >>> 6);
+            const iz = (means_u_data[i * 4 + 2] << 2) | (means_l_data[i * 4 + 2] >>> 6);
+            codes[i] = BigInt(encodeMorton3(ix, iy, iz)) << BigInt(32) | BigInt(i);
+        }
+
+        codes.sort();
+
+        // allocate data for the order buffer, but make it texture-memory sized
+        const order = new Uint32Array(means_l.width * means_l.height);
+        for (let i = 0; i < this.numSplats; ++i) {
+            order[i] = Number(codes[i] & BigInt(0xffffffff));
+        }
+
+        return order;
+    }
+
+    async reorderData() {
+        const { device, height, width } = this.means_l;
+
+        // copy back means_l and means_u data from gpu so cpu reorder has access to it
+        this.means_l._levels[0] = await readImageDataAsync(this.means_l);
+        this.means_u._levels[0] = await readImageDataAsync(this.means_u);
+
+        this.orderTexture = new Texture(device, {
+            name: 'orderTexture',
+            width,
+            height,
+            format: PIXELFORMAT_R32U,
+            mipmaps: false,
+            levels: [this.calcMortonOrder()]
+        });
+
+        device.on('devicerestored', () => {
+            this.reorderGpuMemory();
+        });
+
+        this.reorderGpuMemory();
+    }
 }
 
 export { GSplatSogsData };
diff --git a/src/scene/gsplat/gsplat-sogs.js b/src/scene/gsplat/gsplat-sogs.js
index cac46a325..2486b5133 100644
--- a/src/scene/gsplat/gsplat-sogs.js
+++ b/src/scene/gsplat/gsplat-sogs.js
@@ -42,11 +42,11 @@ class GSplatSogs {
         result.setDefine('GSPLAT_SOGS_DATA', true);
         result.setDefine('SH_BANDS', this.gsplatData.shBands);
 
-        ['means_l', 'means_u', 'quats', 'scales', 'opacities', 'sh0', 'sh_centroids', 'sh_labels_u', 'sh_labels_l'].forEach((name) => {
+        ['means_l', 'means_u', 'quats', 'scales', 'sh0', 'sh_centroids', 'sh_labels'].forEach((name) => {
             result.setParameter(name, gsplatData[name]);
         });
 
-        ['means', 'quats', 'scales', 'opacities', 'sh0', 'shN'].forEach((name) => {
+        ['means', 'scales', 'sh0', 'shN'].forEach((name) => {
             const v = gsplatData.meta[name];
             if (v) {
                 result.setParameter(`${name}_mins`, v.mins);
diff --git a/src/scene/immediate/immediate.js b/src/scene/immediate/immediate.js
index 2cfbdb6ed..2a0d1c689 100644
--- a/src/scene/immediate/immediate.js
+++ b/src/scene/immediate/immediate.js
@@ -5,7 +5,7 @@ import { GraphNode } from '../graph-node.js';
 import { Mesh } from '../mesh.js';
 import { MeshInstance } from '../mesh-instance.js';
 import { ShaderMaterial } from '../materials/shader-material.js';
-import { shaderChunks } from '../shader-lib/chunks/chunks.js';
+import { shaderChunks } from '../shader-lib/chunks-glsl/chunks.js';
 import { shaderChunksWGSL } from '../shader-lib/chunks-wgsl/chunks-wgsl.js';
 import { ImmediateBatches } from './immediate-batches.js';
 
@@ -174,7 +174,7 @@ class Immediate {
         `, /* wgsl */`
 
             varying uv0: vec2f;
-            var colorMap: texture_2d<f32>;
+            var colorMap: texture_2d<uff>;
             @fragment fn fragmentMain(input : FragmentInput) -> FragmentOutput {
                 var output: FragmentOutput;
                 let uv : vec2<i32> = vec2<i32>(input.uv0 * vec2f(textureDimensions(colorMap, 0)));
diff --git a/src/scene/lighting/lights-buffer.js b/src/scene/lighting/lights-buffer.js
index 17756319e..9a39d7df3 100644
--- a/src/scene/lighting/lights-buffer.js
+++ b/src/scene/lighting/lights-buffer.js
@@ -4,7 +4,7 @@ import { FloatPacking } from '../../core/math/float-packing.js';
 import { LIGHTSHAPE_PUNCTUAL, LIGHTTYPE_SPOT, LIGHTSHAPE_RECT, LIGHTSHAPE_DISK, LIGHTSHAPE_SPHERE, LIGHT_COLOR_DIVIDER } from '../constants.js';
 import { Texture } from '../../platform/graphics/texture.js';
 import { LightCamera } from '../renderer/light-camera.js';
-import { shaderChunks } from '../shader-lib/chunks/chunks.js';
+import { shaderChunks } from '../shader-lib/chunks-glsl/chunks.js';
 import { shaderChunksWGSL } from '../shader-lib/chunks-wgsl/chunks-wgsl.js';
 
 const tempVec3 = new Vec3();
diff --git a/src/scene/materials/lit-material.js b/src/scene/materials/lit-material.js
index 854798ef9..d67b65af0 100644
--- a/src/scene/materials/lit-material.js
+++ b/src/scene/materials/lit-material.js
@@ -5,7 +5,7 @@ import { LitMaterialOptions } from './lit-material-options.js';
 import { LitMaterialOptionsBuilder } from './lit-material-options-builder.js';
 import { getProgramLibrary } from '../shader-lib/get-program-library.js';
 import { lit } from '../shader-lib/programs/lit.js';
-import { getCoreDefines } from '../shader-lib/utils.js';
+import { ShaderUtils } from '../shader-lib/shader-utils.js';
 
 const options = new LitMaterialOptions();
 
@@ -92,7 +92,7 @@ class LitMaterial extends Material {
         options.usedUvs = this.usedUvs.slice();
         options.shaderChunkGLSL = this.shaderChunkGLSL;
         options.shaderChunkWGSL = this.shaderChunkWGSL;
-        options.defines = getCoreDefines(this, params);
+        options.defines = ShaderUtils.getCoreDefines(this, params);
 
         LitMaterialOptionsBuilder.update(options.litOptions, this, params.scene, params.cameraShaderParams, params.objDefs, params.pass, params.sortedLights);
         const processingOptions = new ShaderProcessorOptions(params.viewUniformFormat, params.viewBindGroupFormat, params.vertexFormat);
diff --git a/src/scene/materials/shader-material.js b/src/scene/materials/shader-material.js
index 29fbaad57..05e7580ec 100644
--- a/src/scene/materials/shader-material.js
+++ b/src/scene/materials/shader-material.js
@@ -4,7 +4,7 @@ import { ShaderProcessorOptions } from '../../platform/graphics/shader-processor
 import { SHADERDEF_INSTANCING, SHADERDEF_MORPH_NORMAL, SHADERDEF_MORPH_POSITION, SHADERDEF_MORPH_TEXTURE_BASED_INT, SHADERDEF_SKIN } from '../constants.js';
 import { getProgramLibrary } from '../shader-lib/get-program-library.js';
 import { shaderGeneratorShader } from '../shader-lib/programs/shader-generator-shader.js';
-import { getCoreDefines } from '../shader-lib/utils.js';
+import { ShaderUtils } from '../shader-lib/shader-utils.js';
 import { Material } from './material.js';
 
 /**
@@ -132,7 +132,7 @@ class ShaderMaterial extends Material {
 
         const { objDefs } = params;
         const options = {
-            defines: getCoreDefines(this, params),
+            defines: ShaderUtils.getCoreDefines(this, params),
             skin: (objDefs & SHADERDEF_SKIN) !== 0,
             useInstancing: (objDefs & SHADERDEF_INSTANCING) !== 0,
             useMorphPosition: (objDefs & SHADERDEF_MORPH_POSITION) !== 0,
diff --git a/src/scene/materials/standard-material.js b/src/scene/materials/standard-material.js
index 3210f07fa..ed6e1c033 100644
--- a/src/scene/materials/standard-material.js
+++ b/src/scene/materials/standard-material.js
@@ -21,7 +21,7 @@ import { Material } from './material.js';
 import { StandardMaterialOptionsBuilder } from './standard-material-options-builder.js';
 import { standardMaterialCubemapParameters, standardMaterialTextureParameters } from './standard-material-parameters.js';
 import { DebugGraphics } from '../../platform/graphics/debug-graphics.js';
-import { getCoreDefines } from '../shader-lib/utils.js';
+import { ShaderUtils } from '../shader-lib/shader-utils.js';
 
 /**
  * @import { BoundingBox } from '../../core/shape/bounding-box.js'
@@ -837,7 +837,7 @@ class StandardMaterial extends Material {
         const shaderPassInfo = ShaderPass.get(device).getByIndex(pass);
         const minimalOptions = pass === SHADER_PICK || pass === SHADER_PREPASS || shaderPassInfo.isShadow;
         let options = minimalOptions ? standard.optionsContextMin : standard.optionsContext;
-        options.defines = getCoreDefines(this, params);
+        options.defines = ShaderUtils.getCoreDefines(this, params);
 
         if (minimalOptions) {
             this.shaderOptBuilder.updateMinRef(options, scene, this, objDefs, pass, sortedLights);
diff --git a/src/scene/morph-instance.js b/src/scene/morph-instance.js
index 5959bcb17..8a67562c9 100644
--- a/src/scene/morph-instance.js
+++ b/src/scene/morph-instance.js
@@ -1,11 +1,11 @@
 import { Debug } from '../core/debug.js';
-import { SEMANTIC_POSITION, SHADERLANGUAGE_GLSL, SHADERLANGUAGE_WGSL } from '../platform/graphics/constants.js';
+import { SEMANTIC_POSITION } from '../platform/graphics/constants.js';
 import { drawQuadWithShader } from './graphics/quad-render-utils.js';
 import { RenderTarget } from '../platform/graphics/render-target.js';
 import { DebugGraphics } from '../platform/graphics/debug-graphics.js';
-import { createShaderFromCode } from './shader-lib/utils.js';
+import { ShaderUtils } from './shader-lib/shader-utils.js';
 import { BlendState } from '../platform/graphics/blend-state.js';
-import { shaderChunks } from './shader-lib/chunks/chunks.js';
+import { shaderChunks } from './shader-lib/chunks-glsl/chunks.js';
 import { shaderChunksWGSL } from './shader-lib/chunks-wgsl/chunks-wgsl.js';
 
 /**
@@ -191,18 +191,19 @@ class MorphInstance {
      */
     _createShader(maxCount) {
 
-        const wgsl = this.device.isWebGPU;
-        const chunks = wgsl ? shaderChunksWGSL : shaderChunks;
-
         const defines = new Map();
         defines.set('{MORPH_TEXTURE_MAX_COUNT}', maxCount);
         if (this.morph.intRenderFormat) defines.set('MORPH_INT', '');
 
         const outputType = this.morph.intRenderFormat ? 'uvec4' : 'vec4';
-        return createShaderFromCode(this.device, chunks.morphVS, chunks.morphPS, 'TextureMorphShader', {
-            vertex_position: SEMANTIC_POSITION
-        }, {
-            shaderLanguage: wgsl ? SHADERLANGUAGE_WGSL : SHADERLANGUAGE_GLSL,
+
+        return ShaderUtils.createShader(this.device, {
+            uniqueName: 'TextureMorphShader',
+            attributes: { vertex_position: SEMANTIC_POSITION },
+            vertexGLSL: shaderChunks.morphVS,
+            vertexWGSL: shaderChunksWGSL.morphVS,
+            fragmentGLSL: shaderChunks.morphPS,
+            fragmentWGSL: shaderChunksWGSL.morphPS,
             fragmentDefines: defines,
             fragmentOutputTypes: [outputType]
         });
diff --git a/src/scene/particle-system/particle-emitter.js b/src/scene/particle-system/particle-emitter.js
index e8e6d6915..1f566a37a 100644
--- a/src/scene/particle-system/particle-emitter.js
+++ b/src/scene/particle-system/particle-emitter.js
@@ -21,8 +21,6 @@ import {
     typedArrayIndexFormats,
     requiresManualGamma,
     PIXELFORMAT_SRGBA8,
-    SHADERLANGUAGE_WGSL,
-    SHADERLANGUAGE_GLSL,
     SEMANTIC_POSITION
 } from '../../platform/graphics/constants.js';
 import { DeviceCache } from '../../platform/graphics/device-cache.js';
@@ -41,8 +39,8 @@ import {
 } from '../constants.js';
 import { Mesh } from '../mesh.js';
 import { MeshInstance } from '../mesh-instance.js';
-import { createShaderFromCode } from '../shader-lib/utils.js';
-import { shaderChunks } from '../shader-lib/chunks/chunks.js';
+import { ShaderUtils } from '../shader-lib/shader-utils.js';
+import { shaderChunks } from '../shader-lib/chunks-glsl/chunks.js';
 import { shaderChunksWGSL } from '../shader-lib/chunks-wgsl/chunks-wgsl.js';
 import { ParticleCPUUpdater } from './cpu-updater.js';
 import { ParticleGPUUpdater } from './gpu-updater.js';
@@ -361,8 +359,8 @@ class ParticleEmitter {
         this.worldBoundsMul = new Vec3();
         this.worldBoundsAdd = new Vec3();
         this.timeToSwitchBounds = 0;
-        // this.prevPos = new Vec3();
 
+        // simulation shaders - do not destroy those, as they're cached and shared between emitters
         this.shaderParticleUpdateRespawn = null;
         this.shaderParticleUpdateNoRespawn = null;
         this.shaderParticleUpdateOnStop = null;
@@ -596,7 +594,6 @@ class ParticleEmitter {
         this.resetWorldBounds();
 
         if (this.node) {
-            // this.prevPos.copy(this.node.getPosition());
             this.worldBounds.setFromTransformedAabb(
                 this.localBounds, this.localSpace ? Mat4.IDENTITY : this.node.getWorldTransform());
 
@@ -665,40 +662,39 @@ class ParticleEmitter {
         if (this.localSpace) defines.set('LOCAL_SPACE', '');
         if (this.pack8) defines.set('PACK8', '');
         if (this.emitterShape === EMITTERSHAPE_BOX) defines.set('EMITTERSHAPE_BOX', '');
-
-        const shaderLanguage = gd.isWebGPU ? SHADERLANGUAGE_WGSL : SHADERLANGUAGE_GLSL;
-        const chunks = shaderLanguage === SHADERLANGUAGE_WGSL ? shaderChunksWGSL : shaderChunks;
-        const includes = new Map(Object.entries(chunks));
-
-        const shaderCodeRespawn = `#define RESPAWN\n ${chunks.particle_simulationPS}`;
-        const shaderCodeNoRespawn = `#define NO_RESPAWN\n ${chunks.particle_simulationPS}`;
-        const shaderCodeOnStop = `#define ON_STOP\n ${chunks.particle_simulationPS}`;
-
-        // Note: createShaderFromCode can return a shader from the cache (not a new shader) so we *should not* delete these shaders
-        // when the particle emitter is destroyed
-        const params = `Shape:${this.emitterShape}-Pack:${this.pack8}-Local:${this.localSpace}`;
-        this.shaderParticleUpdateRespawn = createShaderFromCode(gd, chunks.fullscreenQuadVS, shaderCodeRespawn, `ParticleUpdateRespawn-${params}`,
-            { vertex_position: SEMANTIC_POSITION }, {
-                fragmentDefines: defines,
-                fragmentIncludes: includes,
-                shaderLanguage: shaderLanguage
-            }
-        );
-        this.shaderParticleUpdateNoRespawn = createShaderFromCode(gd, chunks.fullscreenQuadVS, shaderCodeNoRespawn, `ParticleUpdateNoRespawn-${params}`,
-            { vertex_position: SEMANTIC_POSITION }, {
-                fragmentDefines: defines,
-                fragmentIncludes: includes,
-                shaderLanguage: shaderLanguage
-            }
-        );
-        this.shaderParticleUpdateOnStop = createShaderFromCode(gd, chunks.fullscreenQuadVS, shaderCodeOnStop, `ParticleUpdateStop-${params}`,
-            { vertex_position: SEMANTIC_POSITION }, {
-                fragmentDefines: defines,
-                fragmentIncludes: includes,
-                shaderLanguage: shaderLanguage
-            }
-        );
-
+        const shaderUniqueId = `Shape:${this.emitterShape}-Pack:${this.pack8}-Local:${this.localSpace}`;
+
+        const includes = new Map(Object.entries(gd.isWebGPU ? shaderChunksWGSL : shaderChunks));
+
+        // shader options shared by all 3 shaders
+        const shaderOptions = {
+            attributes: { vertex_position: SEMANTIC_POSITION },
+            vertexGLSL: shaderChunks.fullscreenQuadVS,
+            vertexWGSL: shaderChunksWGSL.fullscreenQuadVS,
+            fragmentGLSL: shaderChunks.particle_simulationPS,
+            fragmentWGSL: shaderChunksWGSL.particle_simulationPS,
+            fragmentDefines: defines,
+            fragmentIncludes: includes
+        };
+
+        // shader 1
+        shaderOptions.uniqueName = `ParticleUpdateRespawn-${shaderUniqueId}`;
+        defines.set('RESPAWN', '');
+        this.shaderParticleUpdateRespawn = ShaderUtils.createShader(gd, shaderOptions);
+        defines.delete('RESPAWN');
+
+        // shader 2
+        shaderOptions.uniqueName = `ParticleUpdateNoRespawn-${shaderUniqueId}`;
+        defines.set('NO_RESPAWN', '');
+        this.shaderParticleUpdateNoRespawn = ShaderUtils.createShader(gd, shaderOptions);
+        defines.delete('NO_RESPAWN');
+
+        // shader 3
+        shaderOptions.uniqueName = `ParticleUpdateStop-${shaderUniqueId}`;
+        defines.set('ON_STOP', '');
+        this.shaderParticleUpdateNoRespawn = ShaderUtils.createShader(gd, shaderOptions);
+
+        // allocate various buffers
         this.numParticleVerts = this.useMesh ? this.mesh.vertexBuffer.numVertices : 4;
         this.numParticleIndices = this.useMesh ? this.mesh.indexBuffer[0].numIndices : 6;
         this._allocate(this.numParticles);
diff --git a/src/scene/particle-system/particle-material.js b/src/scene/particle-system/particle-material.js
index dce8882c3..c2c0e4666 100644
--- a/src/scene/particle-system/particle-material.js
+++ b/src/scene/particle-system/particle-material.js
@@ -10,7 +10,7 @@ import {
 import { getProgramLibrary } from '../shader-lib/get-program-library.js';
 import { Material } from '../materials/material.js';
 import { particle } from '../shader-lib/programs/particle.js';
-import { getCoreDefines } from '../shader-lib/utils.js';
+import { ShaderUtils } from '../shader-lib/shader-utils.js';
 
 /**
  * @import { ParticleEmitter } from './particle-emitter.js'
@@ -41,7 +41,7 @@ class ParticleMaterial extends Material {
         const { device, scene, cameraShaderParams, objDefs } = params;
         const { emitter } = this;
         const options = {
-            defines: getCoreDefines(this, params),
+            defines: ShaderUtils.getCoreDefines(this, params),
             pass: SHADER_FORWARD,
             useCpu: this.emitter.useCpu,
             normal: emitter.lighting ? ((emitter.normalMap !== null) ? 2 : 1) : 0,
diff --git a/src/scene/renderer/render-pass-cookie-renderer.js b/src/scene/renderer/render-pass-cookie-renderer.js
index c984ca427..26c9a20f8 100644
--- a/src/scene/renderer/render-pass-cookie-renderer.js
+++ b/src/scene/renderer/render-pass-cookie-renderer.js
@@ -1,16 +1,16 @@
 import { Debug } from '../../core/debug.js';
 import { Vec4 } from '../../core/math/vec4.js';
 import { Mat4 } from '../../core/math/mat4.js';
-import { CULLFACE_NONE, SEMANTIC_POSITION, SHADERLANGUAGE_GLSL, SHADERLANGUAGE_WGSL } from '../../platform/graphics/constants.js';
+import { CULLFACE_NONE, SEMANTIC_POSITION } from '../../platform/graphics/constants.js';
 import { DebugGraphics } from '../../platform/graphics/debug-graphics.js';
 import { LIGHTTYPE_DIRECTIONAL, LIGHTTYPE_OMNI } from '../constants.js';
-import { createShaderFromCode } from '../shader-lib/utils.js';
+import { ShaderUtils } from '../shader-lib/shader-utils.js';
 import { LightCamera } from './light-camera.js';
 import { BlendState } from '../../platform/graphics/blend-state.js';
 import { QuadRender } from '../graphics/quad-render.js';
 import { DepthState } from '../../platform/graphics/depth-state.js';
 import { RenderPass } from '../../platform/graphics/render-pass.js';
-import { shaderChunks } from '../shader-lib/chunks/chunks.js';
+import { shaderChunks } from '../shader-lib/chunks-glsl/chunks.js';
 import { shaderChunksWGSL } from '../shader-lib/chunks-wgsl/chunks-wgsl.js';
 
 const _viewport = new Vec4();
@@ -112,12 +112,13 @@ class RenderPassCookieRenderer extends RenderPass {
 
     get quadRenderer2D() {
         if (!this._quadRenderer2D) {
-            const wgsl = this.device.isWebGPU;
-            const chunks = wgsl ? shaderChunksWGSL : shaderChunks;
-            const shader = createShaderFromCode(this.device, chunks.cookieBlitVS, chunks.cookieBlit2DPS, 'cookieRenderer2d', {
-                vertex_position: SEMANTIC_POSITION
-            }, {
-                shaderLanguage: wgsl ? SHADERLANGUAGE_WGSL : SHADERLANGUAGE_GLSL
+            const shader = ShaderUtils.createShader(this.device, {
+                uniqueName: 'cookieRenderer2d',
+                attributes: { vertex_position: SEMANTIC_POSITION },
+                vertexGLSL: shaderChunks.cookieBlitVS,
+                vertexWGSL: shaderChunksWGSL.cookieBlitVS,
+                fragmentGLSL: shaderChunks.cookieBlit2DPS,
+                fragmentWGSL: shaderChunksWGSL.cookieBlit2DPS
             });
             this._quadRenderer2D = new QuadRender(shader);
         }
@@ -126,12 +127,13 @@ class RenderPassCookieRenderer extends RenderPass {
 
     get quadRendererCube() {
         if (!this._quadRendererCube) {
-            const wgsl = this.device.isWebGPU;
-            const chunks = wgsl ? shaderChunksWGSL : shaderChunks;
-            const shader = createShaderFromCode(this.device, chunks.cookieBlitVS, chunks.cookieBlitCubePS, 'cookieRendererCube', {
-                vertex_position: SEMANTIC_POSITION
-            }, {
-                shaderLanguage: wgsl ? SHADERLANGUAGE_WGSL : SHADERLANGUAGE_GLSL
+            const shader = ShaderUtils.createShader(this.device, {
+                uniqueName: 'cookieRendererCube',
+                attributes: { vertex_position: SEMANTIC_POSITION },
+                vertexGLSL: shaderChunks.cookieBlitVS,
+                vertexWGSL: shaderChunksWGSL.cookieBlitVS,
+                fragmentGLSL: shaderChunks.cookieBlitCubePS,
+                fragmentWGSL: shaderChunksWGSL.cookieBlitCubePS
             });
             this._quadRendererCube = new QuadRender(shader);
         }
diff --git a/src/scene/renderer/shadow-renderer.js b/src/scene/renderer/shadow-renderer.js
index 1fdc2976b..af6fae5ae 100644
--- a/src/scene/renderer/shadow-renderer.js
+++ b/src/scene/renderer/shadow-renderer.js
@@ -5,7 +5,7 @@ import { Mat4 } from '../../core/math/mat4.js';
 import { Vec3 } from '../../core/math/vec3.js';
 import { Vec4 } from '../../core/math/vec4.js';
 import {
-    SEMANTIC_POSITION, SHADERLANGUAGE_GLSL, SHADERLANGUAGE_WGSL, SHADERSTAGE_FRAGMENT, SHADERSTAGE_VERTEX,
+    SEMANTIC_POSITION, SHADERSTAGE_FRAGMENT, SHADERSTAGE_VERTEX,
     UNIFORMTYPE_MAT4, UNIFORM_BUFFER_DEFAULT_SLOT_NAME
 } from '../../platform/graphics/constants.js';
 import { DebugGraphics } from '../../platform/graphics/debug-graphics.js';
@@ -18,8 +18,8 @@ import {
     shadowTypeInfo
 } from '../constants.js';
 import { ShaderPass } from '../shader-pass.js';
-import { shaderChunks } from '../shader-lib/chunks/chunks.js';
-import { createShaderFromCode } from '../shader-lib/utils.js';
+import { shaderChunks } from '../shader-lib/chunks-glsl/chunks.js';
+import { ShaderUtils } from '../shader-lib/shader-utils.js';
 import { LightCamera } from './light-camera.js';
 import { UniformBufferFormat, UniformFormat } from '../../platform/graphics/uniform-buffer-format.js';
 import { BindUniformBufferFormat, BindGroupFormat } from '../../platform/graphics/bind-group-format.js';
@@ -92,8 +92,6 @@ class ShadowRenderer {
         this.sourceId = scope.resolve('source');
         this.pixelOffsetId = scope.resolve('pixelOffset');
         this.weightId = scope.resolve('weight[0]');
-        const chunks = this.device.isWebGPU ? shaderChunksWGSL : shaderChunks;
-        this.blurVsmShaderCode = [chunks.blurVSMPS, `#define GAUSS\n${chunks.blurVSMPS}`];
 
         // cache for vsm blur shaders
         this.blurVsmShader = [{}, {}];
@@ -495,14 +493,20 @@ class ShadowRenderer {
         if (!blurShader) {
             this.blurVsmWeights[filterSize] = gaussWeights(filterSize);
 
-            const chunks = this.device.isWebGPU ? shaderChunksWGSL : shaderChunks;
-            const blurVS = chunks.fullscreenQuadVS;
-            let blurFS = `#define {SAMPLES} ${filterSize}\n`;
-            blurFS += this.blurVsmShaderCode[blurMode];
-            const blurShaderName = `blurVsm${blurMode}${filterSize}`;
-            blurShader = createShaderFromCode(this.device, blurVS, blurFS, blurShaderName, { vertex_position: SEMANTIC_POSITION }, undefined, {
-                shaderLanguage: this.device.isWebGPU ? SHADERLANGUAGE_WGSL : SHADERLANGUAGE_GLSL
+            const defines = new Map();
+            defines.set('{SAMPLES}', filterSize);
+            if (blurMode === 1) defines.set('GAUSS', '');
+
+            blurShader = ShaderUtils.createShader(this.device, {
+                uniqueName: `blurVsm${blurMode}${filterSize}`,
+                attributes: { vertex_position: SEMANTIC_POSITION },
+                vertexGLSL: shaderChunks.fullscreenQuadVS,
+                vertexWGSL: shaderChunksWGSL.fullscreenQuadVS,
+                fragmentGLSL: shaderChunks.blurVSMPS,
+                fragmentWGSL: shaderChunksWGSL.blurVSMPS,
+                fragmentDefines: defines
             });
+
             cache[blurMode][filterSize] = blurShader;
         }
 
diff --git a/src/scene/shader-lib/chunk-utils.js b/src/scene/shader-lib/chunk-utils.js
index 1299875ca..6d2f09524 100644
--- a/src/scene/shader-lib/chunk-utils.js
+++ b/src/scene/shader-lib/chunk-utils.js
@@ -1,4 +1,4 @@
-import { shaderChunks } from './chunks/chunks.js';
+import { shaderChunks } from './chunks-glsl/chunks.js';
 
 /**
  * @import { CameraShaderParams } from '../camera-shader-params.js'
diff --git a/src/scene/shader-lib/chunks/chunk-validation.js b/src/scene/shader-lib/chunks-glsl/chunk-validation.js
similarity index 100%
rename from src/scene/shader-lib/chunks/chunk-validation.js
rename to src/scene/shader-lib/chunks-glsl/chunk-validation.js
diff --git a/src/scene/shader-lib/chunks/chunks.js b/src/scene/shader-lib/chunks-glsl/chunks.js
similarity index 100%
rename from src/scene/shader-lib/chunks/chunks.js
rename to src/scene/shader-lib/chunks-glsl/chunks.js
diff --git a/src/scene/shader-lib/chunks/common/frag/bayer.js b/src/scene/shader-lib/chunks-glsl/common/frag/bayer.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/bayer.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/bayer.js
diff --git a/src/scene/shader-lib/chunks/common/frag/decode.js b/src/scene/shader-lib/chunks-glsl/common/frag/decode.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/decode.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/decode.js
diff --git a/src/scene/shader-lib/chunks/common/frag/encode.js b/src/scene/shader-lib/chunks-glsl/common/frag/encode.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/encode.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/encode.js
diff --git a/src/scene/shader-lib/chunks/common/frag/envAtlas.js b/src/scene/shader-lib/chunks-glsl/common/frag/envAtlas.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/envAtlas.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/envAtlas.js
diff --git a/src/scene/shader-lib/chunks/common/frag/envProc.js b/src/scene/shader-lib/chunks-glsl/common/frag/envProc.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/envProc.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/envProc.js
diff --git a/src/scene/shader-lib/chunks/common/frag/float-as-uint.js b/src/scene/shader-lib/chunks-glsl/common/frag/float-as-uint.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/float-as-uint.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/float-as-uint.js
diff --git a/src/scene/shader-lib/chunks/common/frag/fog.js b/src/scene/shader-lib/chunks-glsl/common/frag/fog.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/fog.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/fog.js
diff --git a/src/scene/shader-lib/chunks/common/frag/gamma.js b/src/scene/shader-lib/chunks-glsl/common/frag/gamma.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/gamma.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/gamma.js
diff --git a/src/scene/shader-lib/chunks/common/frag/linearizeDepth.js b/src/scene/shader-lib/chunks-glsl/common/frag/linearizeDepth.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/linearizeDepth.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/linearizeDepth.js
diff --git a/src/scene/shader-lib/chunks/common/frag/msdf.js b/src/scene/shader-lib/chunks-glsl/common/frag/msdf.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/msdf.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/msdf.js
diff --git a/src/scene/shader-lib/chunks/common/frag/outputTex2D.js b/src/scene/shader-lib/chunks-glsl/common/frag/outputTex2D.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/outputTex2D.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/outputTex2D.js
diff --git a/src/scene/shader-lib/chunks/common/frag/pick.js b/src/scene/shader-lib/chunks-glsl/common/frag/pick.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/pick.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/pick.js
diff --git a/src/scene/shader-lib/chunks/common/frag/sampleCatmullRom.js b/src/scene/shader-lib/chunks-glsl/common/frag/sampleCatmullRom.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/sampleCatmullRom.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/sampleCatmullRom.js
diff --git a/src/scene/shader-lib/chunks/common/frag/screenDepth.js b/src/scene/shader-lib/chunks-glsl/common/frag/screenDepth.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/screenDepth.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/screenDepth.js
diff --git a/src/scene/shader-lib/chunks/common/frag/spherical.js b/src/scene/shader-lib/chunks-glsl/common/frag/spherical.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/spherical.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/spherical.js
diff --git a/src/scene/shader-lib/chunks/common/frag/tonemapping/tonemapping.js b/src/scene/shader-lib/chunks-glsl/common/frag/tonemapping/tonemapping.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/tonemapping/tonemapping.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/tonemapping/tonemapping.js
diff --git a/src/scene/shader-lib/chunks/common/frag/tonemapping/tonemappingAces.js b/src/scene/shader-lib/chunks-glsl/common/frag/tonemapping/tonemappingAces.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/tonemapping/tonemappingAces.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/tonemapping/tonemappingAces.js
diff --git a/src/scene/shader-lib/chunks/common/frag/tonemapping/tonemappingAces2.js b/src/scene/shader-lib/chunks-glsl/common/frag/tonemapping/tonemappingAces2.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/tonemapping/tonemappingAces2.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/tonemapping/tonemappingAces2.js
diff --git a/src/scene/shader-lib/chunks/common/frag/tonemapping/tonemappingFilmic.js b/src/scene/shader-lib/chunks-glsl/common/frag/tonemapping/tonemappingFilmic.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/tonemapping/tonemappingFilmic.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/tonemapping/tonemappingFilmic.js
diff --git a/src/scene/shader-lib/chunks/common/frag/tonemapping/tonemappingHejl.js b/src/scene/shader-lib/chunks-glsl/common/frag/tonemapping/tonemappingHejl.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/tonemapping/tonemappingHejl.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/tonemapping/tonemappingHejl.js
diff --git a/src/scene/shader-lib/chunks/common/frag/tonemapping/tonemappingLinear.js b/src/scene/shader-lib/chunks-glsl/common/frag/tonemapping/tonemappingLinear.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/tonemapping/tonemappingLinear.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/tonemapping/tonemappingLinear.js
diff --git a/src/scene/shader-lib/chunks/common/frag/tonemapping/tonemappingNeutral.js b/src/scene/shader-lib/chunks-glsl/common/frag/tonemapping/tonemappingNeutral.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/tonemapping/tonemappingNeutral.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/tonemapping/tonemappingNeutral.js
diff --git a/src/scene/shader-lib/chunks/common/frag/tonemapping/tonemappingNone.js b/src/scene/shader-lib/chunks-glsl/common/frag/tonemapping/tonemappingNone.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/frag/tonemapping/tonemappingNone.js
rename to src/scene/shader-lib/chunks-glsl/common/frag/tonemapping/tonemappingNone.js
diff --git a/src/scene/shader-lib/chunks/common/vert/fullscreenQuad.js b/src/scene/shader-lib/chunks-glsl/common/vert/fullscreenQuad.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/vert/fullscreenQuad.js
rename to src/scene/shader-lib/chunks-glsl/common/vert/fullscreenQuad.js
diff --git a/src/scene/shader-lib/chunks/common/vert/msdf.js b/src/scene/shader-lib/chunks-glsl/common/vert/msdf.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/vert/msdf.js
rename to src/scene/shader-lib/chunks-glsl/common/vert/msdf.js
diff --git a/src/scene/shader-lib/chunks/common/vert/normalCore.js b/src/scene/shader-lib/chunks-glsl/common/vert/normalCore.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/vert/normalCore.js
rename to src/scene/shader-lib/chunks-glsl/common/vert/normalCore.js
diff --git a/src/scene/shader-lib/chunks/common/vert/skin.js b/src/scene/shader-lib/chunks-glsl/common/vert/skin.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/vert/skin.js
rename to src/scene/shader-lib/chunks-glsl/common/vert/skin.js
diff --git a/src/scene/shader-lib/chunks/common/vert/skinBatch.js b/src/scene/shader-lib/chunks-glsl/common/vert/skinBatch.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/vert/skinBatch.js
rename to src/scene/shader-lib/chunks-glsl/common/vert/skinBatch.js
diff --git a/src/scene/shader-lib/chunks/common/vert/transform.js b/src/scene/shader-lib/chunks-glsl/common/vert/transform.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/vert/transform.js
rename to src/scene/shader-lib/chunks-glsl/common/vert/transform.js
diff --git a/src/scene/shader-lib/chunks/common/vert/transformCore.js b/src/scene/shader-lib/chunks-glsl/common/vert/transformCore.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/vert/transformCore.js
rename to src/scene/shader-lib/chunks-glsl/common/vert/transformCore.js
diff --git a/src/scene/shader-lib/chunks/common/vert/transformInstancing.js b/src/scene/shader-lib/chunks-glsl/common/vert/transformInstancing.js
similarity index 100%
rename from src/scene/shader-lib/chunks/common/vert/transformInstancing.js
rename to src/scene/shader-lib/chunks-glsl/common/vert/transformInstancing.js
diff --git a/src/scene/shader-lib/chunks/gsplat/frag/gsplat.js b/src/scene/shader-lib/chunks-glsl/gsplat/frag/gsplat.js
similarity index 100%
rename from src/scene/shader-lib/chunks/gsplat/frag/gsplat.js
rename to src/scene/shader-lib/chunks-glsl/gsplat/frag/gsplat.js
diff --git a/src/scene/shader-lib/chunks/gsplat/vert/gsplat.js b/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplat.js
similarity index 100%
rename from src/scene/shader-lib/chunks/gsplat/vert/gsplat.js
rename to src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplat.js
diff --git a/src/scene/shader-lib/chunks/gsplat/vert/gsplatCenter.js b/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatCenter.js
similarity index 100%
rename from src/scene/shader-lib/chunks/gsplat/vert/gsplatCenter.js
rename to src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatCenter.js
diff --git a/src/scene/shader-lib/chunks/gsplat/vert/gsplatColor.js b/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatColor.js
similarity index 100%
rename from src/scene/shader-lib/chunks/gsplat/vert/gsplatColor.js
rename to src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatColor.js
diff --git a/src/scene/shader-lib/chunks/gsplat/vert/gsplatCommon.js b/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatCommon.js
similarity index 89%
rename from src/scene/shader-lib/chunks/gsplat/vert/gsplatCommon.js
rename to src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatCommon.js
index 1a7322693..54bd741ef 100644
--- a/src/scene/shader-lib/chunks/gsplat/vert/gsplatCommon.js
+++ b/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatCommon.js
@@ -13,9 +13,29 @@ struct SplatCenter {
     vec3 view;          // center in view space
     vec4 proj;          // center in clip space
     mat4 modelView;     // model-view matrix
-    float projMat00;    // elememt [0][0] of the projection matrix
+    float projMat00;    // element [0][0] of the projection matrix
 };
 
+mat3 quatToMat3(vec4 R) {
+    vec4 R2 = R + R;
+    float X = R2.x * R.w;
+    vec4 Y  = R2.y * R;
+    vec4 Z  = R2.z * R;
+    float W = R2.w * R.w;
+
+    return mat3(
+        1.0 - Z.z - W,
+              Y.z + X,
+              Y.w - Z.x,
+              Y.z - X,
+        1.0 - Y.y - W,
+              Z.w + Y.x,
+              Y.w + Z.x,
+              Z.w - Y.x,
+        1.0 - Y.y - Z.z
+    );
+}
+
 // stores the offset from center for the current gaussian
 struct SplatCorner {
     vec2 offset;        // corner offset from center in clip space
diff --git a/src/scene/shader-lib/chunks/gsplat/vert/gsplatCompressedData.js b/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatCompressedData.js
similarity index 86%
rename from src/scene/shader-lib/chunks/gsplat/vert/gsplatCompressedData.js
rename to src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatCompressedData.js
index d6b765ed6..089fcb9fa 100644
--- a/src/scene/shader-lib/chunks/gsplat/vert/gsplatCompressedData.js
+++ b/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatCompressedData.js
@@ -42,23 +42,6 @@ vec4 unpackRotation(uint bits) {
     return vec4(a, b, c, m);
 }
 
-mat3 quatToMat3(vec4 R) {
-    float x = R.x;
-    float y = R.y;
-    float z = R.z;
-    float w = R.w;
-    return mat3(
-        1.0 - 2.0 * (z * z + w * w),
-              2.0 * (y * z + x * w),
-              2.0 * (y * w - x * z),
-              2.0 * (y * z - x * w),
-        1.0 - 2.0 * (y * y + w * w),
-              2.0 * (z * w + x * y),
-              2.0 * (y * w + x * z),
-              2.0 * (z * w - x * y),
-        1.0 - 2.0 * (y * y + z * z)
-    );
-}
 
 // read center
 vec3 readCenter(SplatSource source) {
diff --git a/src/scene/shader-lib/chunks/gsplat/vert/gsplatCompressedSH.js b/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatCompressedSH.js
similarity index 100%
rename from src/scene/shader-lib/chunks/gsplat/vert/gsplatCompressedSH.js
rename to src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatCompressedSH.js
diff --git a/src/scene/shader-lib/chunks/gsplat/vert/gsplatCorner.js b/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatCorner.js
similarity index 100%
rename from src/scene/shader-lib/chunks/gsplat/vert/gsplatCorner.js
rename to src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatCorner.js
diff --git a/src/scene/shader-lib/chunks/gsplat/vert/gsplatData.js b/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatData.js
similarity index 67%
rename from src/scene/shader-lib/chunks/gsplat/vert/gsplatData.js
rename to src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatData.js
index 653108712..e44d5f104 100644
--- a/src/scene/shader-lib/chunks/gsplat/vert/gsplatData.js
+++ b/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatData.js
@@ -13,24 +13,6 @@ vec3 readCenter(SplatSource source) {
     return uintBitsToFloat(tA.xyz);
 }
 
-mat3 quatToMat3(vec4 R) {
-    float x = R.w;
-    float y = R.x;
-    float z = R.y;
-    float w = R.z;
-    return mat3(
-        1.0 - 2.0 * (z * z + w * w),
-              2.0 * (y * z + x * w),
-              2.0 * (y * w - x * z),
-              2.0 * (y * z - x * w),
-        1.0 - 2.0 * (y * y + w * w),
-              2.0 * (z * w + x * y),
-              2.0 * (y * w + x * z),
-              2.0 * (z * w - x * y),
-        1.0 - 2.0 * (y * y + z * z)
-    );
-}
-
 vec4 unpackRotation(vec3 packed) {
     return vec4(packed.xyz, sqrt(max(0.0, 1.0 - dot(packed, packed))));
 }
@@ -39,7 +21,7 @@ vec4 unpackRotation(vec3 packed) {
 void readCovariance(in SplatSource source, out vec3 covA, out vec3 covB) {
     vec4 tB = texelFetch(transformB, source.uv, 0);
 
-    mat3 rot = quatToMat3(unpackRotation(vec3(unpackHalf2x16(tAw), tB.w)));
+    mat3 rot = quatToMat3(unpackRotation(vec3(unpackHalf2x16(tAw), tB.w)).wxyz);
     vec3 scale = tB.xyz;
     
     // M = S * R
diff --git a/src/scene/shader-lib/chunks/gsplat/vert/gsplatOutput.js b/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatOutput.js
similarity index 100%
rename from src/scene/shader-lib/chunks/gsplat/vert/gsplatOutput.js
rename to src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatOutput.js
diff --git a/src/scene/shader-lib/chunks/gsplat/vert/gsplatSH.js b/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatSH.js
similarity index 100%
rename from src/scene/shader-lib/chunks/gsplat/vert/gsplatSH.js
rename to src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatSH.js
diff --git a/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatSogsColor.js b/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatSogsColor.js
new file mode 100644
index 000000000..2d4d28048
--- /dev/null
+++ b/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatSogsColor.js
@@ -0,0 +1,13 @@
+export default /* glsl */`
+uniform mediump sampler2D sh0;
+
+uniform vec4 sh0_mins;
+uniform vec4 sh0_maxs;
+
+float SH_C0 = 0.28209479177387814;
+
+vec4 readColor(in SplatSource source) {
+    vec4 clr = mix(sh0_mins, sh0_maxs, texelFetch(sh0, source.uv, 0));
+    return vec4(vec3(0.5) + clr.xyz * SH_C0, 1.0 / (1.0 + exp(-clr.w)));
+}
+`;
diff --git a/src/scene/shader-lib/chunks/gsplat/vert/gsplatSogsData.js b/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatSogsData.js
similarity index 65%
rename from src/scene/shader-lib/chunks/gsplat/vert/gsplatSogsData.js
rename to src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatSogsData.js
index e7c21be98..1f00d31d1 100644
--- a/src/scene/shader-lib/chunks/gsplat/vert/gsplatSogsData.js
+++ b/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatSogsData.js
@@ -23,28 +23,20 @@ vec3 readCenter(SplatSource source) {
     return sign(v) * (exp(abs(v)) - 1.0);
 }
 
-mat3 quatToMat3(vec3 R) {
-    float x = R.x;
-    float y = R.y;
-    float z = R.z;
-    float w2 = clamp(1.0 - (x*x + y*y + z*z), 0.0, 1.0);
-    float w  = sqrt(w2);
-    return mat3(
-        1.0 - 2.0 * (z * z + w2),
-              2.0 * (y * z + x * w),
-              2.0 * (y * w - x * z),
-              2.0 * (y * z - x * w),
-        1.0 - 2.0 * (y * y + w2),
-              2.0 * (z * w + x * y),
-              2.0 * (y * w + x * z),
-              2.0 * (z * w - x * y),
-        1.0 - 2.0 * (y * y + z * z)
-    );
-}
+const float norm = 2.0 / sqrt(2.0);
 
 // sample covariance vectors
 void readCovariance(in SplatSource source, out vec3 covA, out vec3 covB) {
-    vec3 quat = mix(quats_mins, quats_maxs, texelFetch(quats, source.uv, 0).xyz);
+    vec4 qdata = texelFetch(quats, source.uv, 0);
+    vec3 abc = (qdata.xyz - 0.5) * norm;
+    float d = sqrt(max(0.0, 1.0 - dot(abc, abc)));
+
+    uint mode = uint(qdata.w * 255.0 + 0.5) - 252u;
+
+    vec4 quat = (mode == 0u) ? vec4(d, abc) :
+                ((mode == 1u) ? vec4(abc.x, d, abc.yz) :
+                ((mode == 2u) ? vec4(abc.xy, d, abc.z) : vec4(abc, d)));
+
     mat3 rot = quatToMat3(quat);
     vec3 scale = exp(mix(scales_mins, scales_maxs, texelFetch(scales, source.uv, 0).xyz));
 
diff --git a/src/scene/shader-lib/chunks/gsplat/vert/gsplatSogsSH.js b/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatSogsSH.js
similarity index 69%
rename from src/scene/shader-lib/chunks/gsplat/vert/gsplatSogsSH.js
rename to src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatSogsSH.js
index 0ab7d35b3..cb7383a9e 100644
--- a/src/scene/shader-lib/chunks/gsplat/vert/gsplatSogsSH.js
+++ b/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatSogsSH.js
@@ -1,6 +1,5 @@
 export default /* glsl */`
-uniform highp sampler2D sh_labels_u;
-uniform highp sampler2D sh_labels_l;
+uniform highp sampler2D sh_labels;
 uniform highp sampler2D sh_centroids;
 
 uniform float shN_mins;
@@ -8,9 +7,8 @@ uniform float shN_maxs;
 
 void readSHData(in SplatSource source, out vec3 sh[15], out float scale) {
     // extract spherical harmonics palette index
-    int tu = int(texelFetch(sh_labels_u, source.uv, 0).x * 255.0 * 256.0);
-    int tl = int(texelFetch(sh_labels_l, source.uv, 0).x * 255.0);
-    int n = tu + tl;
+    ivec2 t = ivec2(texelFetch(sh_labels, source.uv, 0).xy * 255.0);
+    int n = t.x + t.y * 256;
     int u = (n % 64) * 15;
     int v = n / 64;
 
diff --git a/src/scene/shader-lib/chunks/gsplat/vert/gsplatSource.js b/src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatSource.js
similarity index 100%
rename from src/scene/shader-lib/chunks/gsplat/vert/gsplatSource.js
rename to src/scene/shader-lib/chunks-glsl/gsplat/vert/gsplatSource.js
diff --git a/src/scene/shader-lib/chunks/internal/frag/cookie-blit-2d.js b/src/scene/shader-lib/chunks-glsl/internal/frag/cookie-blit-2d.js
similarity index 100%
rename from src/scene/shader-lib/chunks/internal/frag/cookie-blit-2d.js
rename to src/scene/shader-lib/chunks-glsl/internal/frag/cookie-blit-2d.js
diff --git a/src/scene/shader-lib/chunks/internal/frag/cookie-blit-cube.js b/src/scene/shader-lib/chunks-glsl/internal/frag/cookie-blit-cube.js
similarity index 100%
rename from src/scene/shader-lib/chunks/internal/frag/cookie-blit-cube.js
rename to src/scene/shader-lib/chunks-glsl/internal/frag/cookie-blit-cube.js
diff --git a/src/scene/shader-lib/chunks/internal/frag/immediateLine.js b/src/scene/shader-lib/chunks-glsl/internal/frag/immediateLine.js
similarity index 100%
rename from src/scene/shader-lib/chunks/internal/frag/immediateLine.js
rename to src/scene/shader-lib/chunks-glsl/internal/frag/immediateLine.js
diff --git a/src/scene/shader-lib/chunks/internal/frag/reproject.js b/src/scene/shader-lib/chunks-glsl/internal/frag/reproject.js
similarity index 100%
rename from src/scene/shader-lib/chunks/internal/frag/reproject.js
rename to src/scene/shader-lib/chunks-glsl/internal/frag/reproject.js
diff --git a/src/scene/shader-lib/chunks/internal/morph/frag/morph.js b/src/scene/shader-lib/chunks-glsl/internal/morph/frag/morph.js
similarity index 100%
rename from src/scene/shader-lib/chunks/internal/morph/frag/morph.js
rename to src/scene/shader-lib/chunks-glsl/internal/morph/frag/morph.js
diff --git a/src/scene/shader-lib/chunks/internal/morph/vert/morph.js b/src/scene/shader-lib/chunks-glsl/internal/morph/vert/morph.js
similarity index 100%
rename from src/scene/shader-lib/chunks/internal/morph/vert/morph.js
rename to src/scene/shader-lib/chunks-glsl/internal/morph/vert/morph.js
diff --git a/src/scene/shader-lib/chunks/internal/vert/cookie-blit.js b/src/scene/shader-lib/chunks-glsl/internal/vert/cookie-blit.js
similarity index 100%
rename from src/scene/shader-lib/chunks/internal/vert/cookie-blit.js
rename to src/scene/shader-lib/chunks-glsl/internal/vert/cookie-blit.js
diff --git a/src/scene/shader-lib/chunks/internal/vert/immediateLine.js b/src/scene/shader-lib/chunks-glsl/internal/vert/immediateLine.js
similarity index 100%
rename from src/scene/shader-lib/chunks/internal/vert/immediateLine.js
rename to src/scene/shader-lib/chunks-glsl/internal/vert/immediateLine.js
diff --git a/src/scene/shader-lib/chunks/internal/vert/reproject.js b/src/scene/shader-lib/chunks-glsl/internal/vert/reproject.js
similarity index 100%
rename from src/scene/shader-lib/chunks/internal/vert/reproject.js
rename to src/scene/shader-lib/chunks-glsl/internal/vert/reproject.js
diff --git a/src/scene/shader-lib/chunks/lightmapper/frag/bakeDirLmEnd.js b/src/scene/shader-lib/chunks-glsl/lightmapper/frag/bakeDirLmEnd.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lightmapper/frag/bakeDirLmEnd.js
rename to src/scene/shader-lib/chunks-glsl/lightmapper/frag/bakeDirLmEnd.js
diff --git a/src/scene/shader-lib/chunks/lightmapper/frag/bakeLmEnd.js b/src/scene/shader-lib/chunks-glsl/lightmapper/frag/bakeLmEnd.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lightmapper/frag/bakeLmEnd.js
rename to src/scene/shader-lib/chunks-glsl/lightmapper/frag/bakeLmEnd.js
diff --git a/src/scene/shader-lib/chunks/lightmapper/frag/bilateralDeNoise.js b/src/scene/shader-lib/chunks-glsl/lightmapper/frag/bilateralDeNoise.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lightmapper/frag/bilateralDeNoise.js
rename to src/scene/shader-lib/chunks-glsl/lightmapper/frag/bilateralDeNoise.js
diff --git a/src/scene/shader-lib/chunks/lightmapper/frag/dilate.js b/src/scene/shader-lib/chunks-glsl/lightmapper/frag/dilate.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lightmapper/frag/dilate.js
rename to src/scene/shader-lib/chunks-glsl/lightmapper/frag/dilate.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/TBN.js b/src/scene/shader-lib/chunks-glsl/lit/frag/TBN.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/TBN.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/TBN.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/ambient.js b/src/scene/shader-lib/chunks-glsl/lit/frag/ambient.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/ambient.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/ambient.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/aoDiffuseOcc.js b/src/scene/shader-lib/chunks-glsl/lit/frag/aoDiffuseOcc.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/aoDiffuseOcc.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/aoDiffuseOcc.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/aoSpecOcc.js b/src/scene/shader-lib/chunks-glsl/lit/frag/aoSpecOcc.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/aoSpecOcc.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/aoSpecOcc.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/base.js b/src/scene/shader-lib/chunks-glsl/lit/frag/base.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/base.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/base.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/baseNineSliced.js b/src/scene/shader-lib/chunks-glsl/lit/frag/baseNineSliced.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/baseNineSliced.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/baseNineSliced.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/baseNineSlicedTiled.js b/src/scene/shader-lib/chunks-glsl/lit/frag/baseNineSlicedTiled.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/baseNineSlicedTiled.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/baseNineSlicedTiled.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/blurVSM.js b/src/scene/shader-lib/chunks-glsl/lit/frag/blurVSM.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/blurVSM.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/blurVSM.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/clusteredLight.js b/src/scene/shader-lib/chunks-glsl/lit/frag/clusteredLight.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/clusteredLight.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/clusteredLight.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/clusteredLightCookies.js b/src/scene/shader-lib/chunks-glsl/lit/frag/clusteredLightCookies.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/clusteredLightCookies.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/clusteredLightCookies.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/clusteredLightShadows.js b/src/scene/shader-lib/chunks-glsl/lit/frag/clusteredLightShadows.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/clusteredLightShadows.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/clusteredLightShadows.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/clusteredLightUtils.js b/src/scene/shader-lib/chunks-glsl/lit/frag/clusteredLightUtils.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/clusteredLightUtils.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/clusteredLightUtils.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/combine.js b/src/scene/shader-lib/chunks-glsl/lit/frag/combine.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/combine.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/combine.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/cookie.js b/src/scene/shader-lib/chunks-glsl/lit/frag/cookie.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/cookie.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/cookie.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/cubeMapProject.js b/src/scene/shader-lib/chunks-glsl/lit/frag/cubeMapProject.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/cubeMapProject.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/cubeMapProject.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/cubeMapRotate.js b/src/scene/shader-lib/chunks-glsl/lit/frag/cubeMapRotate.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/cubeMapRotate.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/cubeMapRotate.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/debug-output.js b/src/scene/shader-lib/chunks-glsl/lit/frag/debug-output.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/debug-output.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/debug-output.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/debug-process-frontend.js b/src/scene/shader-lib/chunks-glsl/lit/frag/debug-process-frontend.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/debug-process-frontend.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/debug-process-frontend.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/end.js b/src/scene/shader-lib/chunks-glsl/lit/frag/end.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/end.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/end.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/falloffInvSquared.js b/src/scene/shader-lib/chunks-glsl/lit/frag/falloffInvSquared.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/falloffInvSquared.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/falloffInvSquared.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/falloffLinear.js b/src/scene/shader-lib/chunks-glsl/lit/frag/falloffLinear.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/falloffLinear.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/falloffLinear.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/fresnelSchlick.js b/src/scene/shader-lib/chunks-glsl/lit/frag/fresnelSchlick.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/fresnelSchlick.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/fresnelSchlick.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/iridescenceDiffraction.js b/src/scene/shader-lib/chunks-glsl/lit/frag/iridescenceDiffraction.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/iridescenceDiffraction.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/iridescenceDiffraction.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/lightDiffuseLambert.js b/src/scene/shader-lib/chunks-glsl/lit/frag/lightDiffuseLambert.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/lightDiffuseLambert.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/lightDiffuseLambert.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/lightDirPoint.js b/src/scene/shader-lib/chunks-glsl/lit/frag/lightDirPoint.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/lightDirPoint.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/lightDirPoint.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/lightSheen.js b/src/scene/shader-lib/chunks-glsl/lit/frag/lightSheen.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/lightSheen.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/lightSheen.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/lightSpecularAnisoGGX.js b/src/scene/shader-lib/chunks-glsl/lit/frag/lightSpecularAnisoGGX.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/lightSpecularAnisoGGX.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/lightSpecularAnisoGGX.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/lightSpecularBlinn.js b/src/scene/shader-lib/chunks-glsl/lit/frag/lightSpecularBlinn.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/lightSpecularBlinn.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/lightSpecularBlinn.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/lighting/lightDeclaration.js b/src/scene/shader-lib/chunks-glsl/lit/frag/lighting/lightDeclaration.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/lighting/lightDeclaration.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/lighting/lightDeclaration.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/lighting/lightEvaluation.js b/src/scene/shader-lib/chunks-glsl/lit/frag/lighting/lightEvaluation.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/lighting/lightEvaluation.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/lighting/lightEvaluation.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/lighting/lightFunctionLight.js b/src/scene/shader-lib/chunks-glsl/lit/frag/lighting/lightFunctionLight.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/lighting/lightFunctionLight.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/lighting/lightFunctionLight.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/lighting/lightFunctionShadow.js b/src/scene/shader-lib/chunks-glsl/lit/frag/lighting/lightFunctionShadow.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/lighting/lightFunctionShadow.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/lighting/lightFunctionShadow.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/lighting/lighting.js b/src/scene/shader-lib/chunks-glsl/lit/frag/lighting/lighting.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/lighting/lighting.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/lighting/lighting.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/lighting/shadowCascades.js b/src/scene/shader-lib/chunks-glsl/lit/frag/lighting/shadowCascades.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/lighting/shadowCascades.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/lighting/shadowCascades.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/lighting/shadowEVSM.js b/src/scene/shader-lib/chunks-glsl/lit/frag/lighting/shadowEVSM.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/lighting/shadowEVSM.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/lighting/shadowEVSM.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/lighting/shadowPCF1.js b/src/scene/shader-lib/chunks-glsl/lit/frag/lighting/shadowPCF1.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/lighting/shadowPCF1.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/lighting/shadowPCF1.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/lighting/shadowPCF3.js b/src/scene/shader-lib/chunks-glsl/lit/frag/lighting/shadowPCF3.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/lighting/shadowPCF3.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/lighting/shadowPCF3.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/lighting/shadowPCF5.js b/src/scene/shader-lib/chunks-glsl/lit/frag/lighting/shadowPCF5.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/lighting/shadowPCF5.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/lighting/shadowPCF5.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/lighting/shadowPCSS.js b/src/scene/shader-lib/chunks-glsl/lit/frag/lighting/shadowPCSS.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/lighting/shadowPCSS.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/lighting/shadowPCSS.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/lighting/shadowSoft.js b/src/scene/shader-lib/chunks-glsl/lit/frag/lighting/shadowSoft.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/lighting/shadowSoft.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/lighting/shadowSoft.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/lightmapAdd.js b/src/scene/shader-lib/chunks-glsl/lit/frag/lightmapAdd.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/lightmapAdd.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/lightmapAdd.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/litMain.js b/src/scene/shader-lib/chunks-glsl/lit/frag/litMain.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/litMain.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/litMain.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/ltc.js b/src/scene/shader-lib/chunks-glsl/lit/frag/ltc.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/ltc.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/ltc.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/metalnessModulate.js b/src/scene/shader-lib/chunks-glsl/lit/frag/metalnessModulate.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/metalnessModulate.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/metalnessModulate.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/output.js b/src/scene/shader-lib/chunks-glsl/lit/frag/output.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/output.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/output.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/outputAlpha.js b/src/scene/shader-lib/chunks-glsl/lit/frag/outputAlpha.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/outputAlpha.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/outputAlpha.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/pass-forward/litForwardBackend.js b/src/scene/shader-lib/chunks-glsl/lit/frag/pass-forward/litForwardBackend.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/pass-forward/litForwardBackend.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/pass-forward/litForwardBackend.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/pass-forward/litForwardDeclaration.js b/src/scene/shader-lib/chunks-glsl/lit/frag/pass-forward/litForwardDeclaration.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/pass-forward/litForwardDeclaration.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/pass-forward/litForwardDeclaration.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/pass-forward/litForwardMain.js b/src/scene/shader-lib/chunks-glsl/lit/frag/pass-forward/litForwardMain.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/pass-forward/litForwardMain.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/pass-forward/litForwardMain.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/pass-forward/litForwardPostCode.js b/src/scene/shader-lib/chunks-glsl/lit/frag/pass-forward/litForwardPostCode.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/pass-forward/litForwardPostCode.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/pass-forward/litForwardPostCode.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/pass-forward/litForwardPreCode.js b/src/scene/shader-lib/chunks-glsl/lit/frag/pass-forward/litForwardPreCode.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/pass-forward/litForwardPreCode.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/pass-forward/litForwardPreCode.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/pass-other/litOtherMain.js b/src/scene/shader-lib/chunks-glsl/lit/frag/pass-other/litOtherMain.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/pass-other/litOtherMain.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/pass-other/litOtherMain.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/pass-shadow/litShadowMain.js b/src/scene/shader-lib/chunks-glsl/lit/frag/pass-shadow/litShadowMain.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/pass-shadow/litShadowMain.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/pass-shadow/litShadowMain.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/reflDir.js b/src/scene/shader-lib/chunks-glsl/lit/frag/reflDir.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/reflDir.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/reflDir.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/reflDirAniso.js b/src/scene/shader-lib/chunks-glsl/lit/frag/reflDirAniso.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/reflDirAniso.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/reflDirAniso.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/reflectionCC.js b/src/scene/shader-lib/chunks-glsl/lit/frag/reflectionCC.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/reflectionCC.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/reflectionCC.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/reflectionCube.js b/src/scene/shader-lib/chunks-glsl/lit/frag/reflectionCube.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/reflectionCube.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/reflectionCube.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/reflectionEnv.js b/src/scene/shader-lib/chunks-glsl/lit/frag/reflectionEnv.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/reflectionEnv.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/reflectionEnv.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/reflectionEnvHQ.js b/src/scene/shader-lib/chunks-glsl/lit/frag/reflectionEnvHQ.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/reflectionEnvHQ.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/reflectionEnvHQ.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/reflectionSheen.js b/src/scene/shader-lib/chunks-glsl/lit/frag/reflectionSheen.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/reflectionSheen.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/reflectionSheen.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/reflectionSphere.js b/src/scene/shader-lib/chunks-glsl/lit/frag/reflectionSphere.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/reflectionSphere.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/reflectionSphere.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/refractionCube.js b/src/scene/shader-lib/chunks-glsl/lit/frag/refractionCube.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/refractionCube.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/refractionCube.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/refractionDynamic.js b/src/scene/shader-lib/chunks-glsl/lit/frag/refractionDynamic.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/refractionDynamic.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/refractionDynamic.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/spot.js b/src/scene/shader-lib/chunks-glsl/lit/frag/spot.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/spot.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/spot.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/startNineSliced.js b/src/scene/shader-lib/chunks-glsl/lit/frag/startNineSliced.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/startNineSliced.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/startNineSliced.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/startNineSlicedTiled.js b/src/scene/shader-lib/chunks-glsl/lit/frag/startNineSlicedTiled.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/startNineSlicedTiled.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/startNineSlicedTiled.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/twoSidedLighting.js b/src/scene/shader-lib/chunks-glsl/lit/frag/twoSidedLighting.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/twoSidedLighting.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/twoSidedLighting.js
diff --git a/src/scene/shader-lib/chunks/lit/frag/viewDir.js b/src/scene/shader-lib/chunks-glsl/lit/frag/viewDir.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/frag/viewDir.js
rename to src/scene/shader-lib/chunks-glsl/lit/frag/viewDir.js
diff --git a/src/scene/shader-lib/chunks/lit/vert/litMain.js b/src/scene/shader-lib/chunks-glsl/lit/vert/litMain.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/vert/litMain.js
rename to src/scene/shader-lib/chunks-glsl/lit/vert/litMain.js
diff --git a/src/scene/shader-lib/chunks/lit/vert/normal.js b/src/scene/shader-lib/chunks-glsl/lit/vert/normal.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/vert/normal.js
rename to src/scene/shader-lib/chunks-glsl/lit/vert/normal.js
diff --git a/src/scene/shader-lib/chunks/lit/vert/uv0.js b/src/scene/shader-lib/chunks-glsl/lit/vert/uv0.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/vert/uv0.js
rename to src/scene/shader-lib/chunks-glsl/lit/vert/uv0.js
diff --git a/src/scene/shader-lib/chunks/lit/vert/uv1.js b/src/scene/shader-lib/chunks-glsl/lit/vert/uv1.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/vert/uv1.js
rename to src/scene/shader-lib/chunks-glsl/lit/vert/uv1.js
diff --git a/src/scene/shader-lib/chunks/lit/vert/uvTransform.js b/src/scene/shader-lib/chunks-glsl/lit/vert/uvTransform.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/vert/uvTransform.js
rename to src/scene/shader-lib/chunks-glsl/lit/vert/uvTransform.js
diff --git a/src/scene/shader-lib/chunks/lit/vert/uvTransformUniforms.js b/src/scene/shader-lib/chunks-glsl/lit/vert/uvTransformUniforms.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/vert/uvTransformUniforms.js
rename to src/scene/shader-lib/chunks-glsl/lit/vert/uvTransformUniforms.js
diff --git a/src/scene/shader-lib/chunks/lit/vert/viewNormal.js b/src/scene/shader-lib/chunks-glsl/lit/vert/viewNormal.js
similarity index 100%
rename from src/scene/shader-lib/chunks/lit/vert/viewNormal.js
rename to src/scene/shader-lib/chunks-glsl/lit/vert/viewNormal.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particle-shader.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particle-shader.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particle-shader.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particle-shader.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particle-simulation.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particle-simulation.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particle-simulation.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particle-simulation.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particle.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particle.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particle.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particle.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particleInputFloat.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particleInputFloat.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particleInputFloat.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particleInputFloat.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particleInputRgba8.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particleInputRgba8.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particleInputRgba8.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particleInputRgba8.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particleOutputFloat.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particleOutputFloat.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particleOutputFloat.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particleOutputFloat.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particleOutputRgba8.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particleOutputRgba8.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particleOutputRgba8.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particleOutputRgba8.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particleUpdaterAABB.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particleUpdaterAABB.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particleUpdaterAABB.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particleUpdaterAABB.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particleUpdaterEnd.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particleUpdaterEnd.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particleUpdaterEnd.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particleUpdaterEnd.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particleUpdaterInit.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particleUpdaterInit.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particleUpdaterInit.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particleUpdaterInit.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particleUpdaterNoRespawn.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particleUpdaterNoRespawn.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particleUpdaterNoRespawn.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particleUpdaterNoRespawn.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particleUpdaterOnStop.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particleUpdaterOnStop.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particleUpdaterOnStop.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particleUpdaterOnStop.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particleUpdaterRespawn.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particleUpdaterRespawn.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particleUpdaterRespawn.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particleUpdaterRespawn.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particleUpdaterSphere.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particleUpdaterSphere.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particleUpdaterSphere.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particleUpdaterSphere.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particleUpdaterStart.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particleUpdaterStart.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particleUpdaterStart.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particleUpdaterStart.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particle_blendAdd.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particle_blendAdd.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particle_blendAdd.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particle_blendAdd.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particle_blendMultiply.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particle_blendMultiply.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particle_blendMultiply.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particle_blendMultiply.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particle_blendNormal.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particle_blendNormal.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particle_blendNormal.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particle_blendNormal.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particle_end.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particle_end.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particle_end.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particle_end.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particle_halflambert.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particle_halflambert.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particle_halflambert.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particle_halflambert.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particle_lambert.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particle_lambert.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particle_lambert.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particle_lambert.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particle_lighting.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particle_lighting.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particle_lighting.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particle_lighting.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particle_normalMap.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particle_normalMap.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particle_normalMap.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particle_normalMap.js
diff --git a/src/scene/shader-lib/chunks/particle/frag/particle_soft.js b/src/scene/shader-lib/chunks-glsl/particle/frag/particle_soft.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/frag/particle_soft.js
rename to src/scene/shader-lib/chunks-glsl/particle/frag/particle_soft.js
diff --git a/src/scene/shader-lib/chunks/particle/vert/particle-shader.js b/src/scene/shader-lib/chunks-glsl/particle/vert/particle-shader.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/vert/particle-shader.js
rename to src/scene/shader-lib/chunks-glsl/particle/vert/particle-shader.js
diff --git a/src/scene/shader-lib/chunks/particle/vert/particle.js b/src/scene/shader-lib/chunks-glsl/particle/vert/particle.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/vert/particle.js
rename to src/scene/shader-lib/chunks-glsl/particle/vert/particle.js
diff --git a/src/scene/shader-lib/chunks/particle/vert/particleAnimFrameClamp.js b/src/scene/shader-lib/chunks-glsl/particle/vert/particleAnimFrameClamp.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/vert/particleAnimFrameClamp.js
rename to src/scene/shader-lib/chunks-glsl/particle/vert/particleAnimFrameClamp.js
diff --git a/src/scene/shader-lib/chunks/particle/vert/particleAnimFrameLoop.js b/src/scene/shader-lib/chunks-glsl/particle/vert/particleAnimFrameLoop.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/vert/particleAnimFrameLoop.js
rename to src/scene/shader-lib/chunks-glsl/particle/vert/particleAnimFrameLoop.js
diff --git a/src/scene/shader-lib/chunks/particle/vert/particleAnimTex.js b/src/scene/shader-lib/chunks-glsl/particle/vert/particleAnimTex.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/vert/particleAnimTex.js
rename to src/scene/shader-lib/chunks-glsl/particle/vert/particleAnimTex.js
diff --git a/src/scene/shader-lib/chunks/particle/vert/particle_TBN.js b/src/scene/shader-lib/chunks-glsl/particle/vert/particle_TBN.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/vert/particle_TBN.js
rename to src/scene/shader-lib/chunks-glsl/particle/vert/particle_TBN.js
diff --git a/src/scene/shader-lib/chunks/particle/vert/particle_billboard.js b/src/scene/shader-lib/chunks-glsl/particle/vert/particle_billboard.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/vert/particle_billboard.js
rename to src/scene/shader-lib/chunks-glsl/particle/vert/particle_billboard.js
diff --git a/src/scene/shader-lib/chunks/particle/vert/particle_cpu.js b/src/scene/shader-lib/chunks-glsl/particle/vert/particle_cpu.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/vert/particle_cpu.js
rename to src/scene/shader-lib/chunks-glsl/particle/vert/particle_cpu.js
diff --git a/src/scene/shader-lib/chunks/particle/vert/particle_cpu_end.js b/src/scene/shader-lib/chunks-glsl/particle/vert/particle_cpu_end.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/vert/particle_cpu_end.js
rename to src/scene/shader-lib/chunks-glsl/particle/vert/particle_cpu_end.js
diff --git a/src/scene/shader-lib/chunks/particle/vert/particle_customFace.js b/src/scene/shader-lib/chunks-glsl/particle/vert/particle_customFace.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/vert/particle_customFace.js
rename to src/scene/shader-lib/chunks-glsl/particle/vert/particle_customFace.js
diff --git a/src/scene/shader-lib/chunks/particle/vert/particle_end.js b/src/scene/shader-lib/chunks-glsl/particle/vert/particle_end.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/vert/particle_end.js
rename to src/scene/shader-lib/chunks-glsl/particle/vert/particle_end.js
diff --git a/src/scene/shader-lib/chunks/particle/vert/particle_init.js b/src/scene/shader-lib/chunks-glsl/particle/vert/particle_init.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/vert/particle_init.js
rename to src/scene/shader-lib/chunks-glsl/particle/vert/particle_init.js
diff --git a/src/scene/shader-lib/chunks/particle/vert/particle_localShift.js b/src/scene/shader-lib/chunks-glsl/particle/vert/particle_localShift.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/vert/particle_localShift.js
rename to src/scene/shader-lib/chunks-glsl/particle/vert/particle_localShift.js
diff --git a/src/scene/shader-lib/chunks/particle/vert/particle_mesh.js b/src/scene/shader-lib/chunks-glsl/particle/vert/particle_mesh.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/vert/particle_mesh.js
rename to src/scene/shader-lib/chunks-glsl/particle/vert/particle_mesh.js
diff --git a/src/scene/shader-lib/chunks/particle/vert/particle_normal.js b/src/scene/shader-lib/chunks-glsl/particle/vert/particle_normal.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/vert/particle_normal.js
rename to src/scene/shader-lib/chunks-glsl/particle/vert/particle_normal.js
diff --git a/src/scene/shader-lib/chunks/particle/vert/particle_pointAlong.js b/src/scene/shader-lib/chunks-glsl/particle/vert/particle_pointAlong.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/vert/particle_pointAlong.js
rename to src/scene/shader-lib/chunks-glsl/particle/vert/particle_pointAlong.js
diff --git a/src/scene/shader-lib/chunks/particle/vert/particle_soft.js b/src/scene/shader-lib/chunks-glsl/particle/vert/particle_soft.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/vert/particle_soft.js
rename to src/scene/shader-lib/chunks-glsl/particle/vert/particle_soft.js
diff --git a/src/scene/shader-lib/chunks/particle/vert/particle_stretch.js b/src/scene/shader-lib/chunks-glsl/particle/vert/particle_stretch.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/vert/particle_stretch.js
rename to src/scene/shader-lib/chunks-glsl/particle/vert/particle_stretch.js
diff --git a/src/scene/shader-lib/chunks/particle/vert/particle_wrap.js b/src/scene/shader-lib/chunks-glsl/particle/vert/particle_wrap.js
similarity index 100%
rename from src/scene/shader-lib/chunks/particle/vert/particle_wrap.js
rename to src/scene/shader-lib/chunks-glsl/particle/vert/particle_wrap.js
diff --git a/src/scene/shader-lib/chunks/skybox/frag/skybox.js b/src/scene/shader-lib/chunks-glsl/skybox/frag/skybox.js
similarity index 100%
rename from src/scene/shader-lib/chunks/skybox/frag/skybox.js
rename to src/scene/shader-lib/chunks-glsl/skybox/frag/skybox.js
diff --git a/src/scene/shader-lib/chunks/skybox/vert/skybox.js b/src/scene/shader-lib/chunks-glsl/skybox/vert/skybox.js
similarity index 100%
rename from src/scene/shader-lib/chunks/skybox/vert/skybox.js
rename to src/scene/shader-lib/chunks-glsl/skybox/vert/skybox.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/alphaTest.js b/src/scene/shader-lib/chunks-glsl/standard/frag/alphaTest.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/alphaTest.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/alphaTest.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/ao.js b/src/scene/shader-lib/chunks-glsl/standard/frag/ao.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/ao.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/ao.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/clearCoat.js b/src/scene/shader-lib/chunks-glsl/standard/frag/clearCoat.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/clearCoat.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/clearCoat.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/clearCoatGloss.js b/src/scene/shader-lib/chunks-glsl/standard/frag/clearCoatGloss.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/clearCoatGloss.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/clearCoatGloss.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/clearCoatNormal.js b/src/scene/shader-lib/chunks-glsl/standard/frag/clearCoatNormal.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/clearCoatNormal.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/clearCoatNormal.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/detailModes.js b/src/scene/shader-lib/chunks-glsl/standard/frag/detailModes.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/detailModes.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/detailModes.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/diffuse.js b/src/scene/shader-lib/chunks-glsl/standard/frag/diffuse.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/diffuse.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/diffuse.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/emissive.js b/src/scene/shader-lib/chunks-glsl/standard/frag/emissive.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/emissive.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/emissive.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/gloss.js b/src/scene/shader-lib/chunks-glsl/standard/frag/gloss.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/gloss.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/gloss.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/ior.js b/src/scene/shader-lib/chunks-glsl/standard/frag/ior.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/ior.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/ior.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/iridescence.js b/src/scene/shader-lib/chunks-glsl/standard/frag/iridescence.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/iridescence.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/iridescence.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/iridescenceThickness.js b/src/scene/shader-lib/chunks-glsl/standard/frag/iridescenceThickness.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/iridescenceThickness.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/iridescenceThickness.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/lightmap.js b/src/scene/shader-lib/chunks-glsl/standard/frag/lightmap.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/lightmap.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/lightmap.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/litShaderArgs.js b/src/scene/shader-lib/chunks-glsl/standard/frag/litShaderArgs.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/litShaderArgs.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/litShaderArgs.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/litShaderCore.js b/src/scene/shader-lib/chunks-glsl/standard/frag/litShaderCore.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/litShaderCore.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/litShaderCore.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/metalness.js b/src/scene/shader-lib/chunks-glsl/standard/frag/metalness.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/metalness.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/metalness.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/normalMap.js b/src/scene/shader-lib/chunks-glsl/standard/frag/normalMap.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/normalMap.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/normalMap.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/opacity-dither.js b/src/scene/shader-lib/chunks-glsl/standard/frag/opacity-dither.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/opacity-dither.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/opacity-dither.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/opacity.js b/src/scene/shader-lib/chunks-glsl/standard/frag/opacity.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/opacity.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/opacity.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/parallax.js b/src/scene/shader-lib/chunks-glsl/standard/frag/parallax.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/parallax.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/parallax.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/sheen.js b/src/scene/shader-lib/chunks-glsl/standard/frag/sheen.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/sheen.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/sheen.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/sheenGloss.js b/src/scene/shader-lib/chunks-glsl/standard/frag/sheenGloss.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/sheenGloss.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/sheenGloss.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/specular.js b/src/scene/shader-lib/chunks-glsl/standard/frag/specular.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/specular.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/specular.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/specularityFactor.js b/src/scene/shader-lib/chunks-glsl/standard/frag/specularityFactor.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/specularityFactor.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/specularityFactor.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/stdDeclaration.js b/src/scene/shader-lib/chunks-glsl/standard/frag/stdDeclaration.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/stdDeclaration.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/stdDeclaration.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/stdFrontEnd.js b/src/scene/shader-lib/chunks-glsl/standard/frag/stdFrontEnd.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/stdFrontEnd.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/stdFrontEnd.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/thickness.js b/src/scene/shader-lib/chunks-glsl/standard/frag/thickness.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/thickness.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/thickness.js
diff --git a/src/scene/shader-lib/chunks/standard/frag/transmission.js b/src/scene/shader-lib/chunks-glsl/standard/frag/transmission.js
similarity index 100%
rename from src/scene/shader-lib/chunks/standard/frag/transmission.js
rename to src/scene/shader-lib/chunks-glsl/standard/frag/transmission.js
diff --git a/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplatCommon.js b/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplatCommon.js
index d58094957..5fedb04e2 100644
--- a/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplatCommon.js
+++ b/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplatCommon.js
@@ -25,6 +25,20 @@ struct SplatCorner {
     #endif
 }
 
+fn quatToMat3(R: vec4<f32>) -> mat3x3<f32> {
+    let R2: vec4<f32> = R + R;
+    let X: f32       = R2.x * R.w;
+    let Y: vec4<f32> = R2.y * R;
+    let Z: vec4<f32> = R2.z * R;
+    let W: f32       = R2.w * R.w;
+
+    return mat3x3<f32>(
+        1.0 - Z.z - W,  Y.z + X,      Y.w - Z.x,
+        Y.z - X,        1.0 - Y.y - W, Z.w + Y.x,
+        Y.w + Z.x,      Z.w - Y.x,     1.0 - Y.y - Z.z
+    );
+}
+
 #if GSPLAT_COMPRESSED_DATA == true
     #include "gsplatCompressedDataVS"
     #include "gsplatCompressedSHVS"
diff --git a/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplatCompressedData.js b/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplatCompressedData.js
index 487f4d6bd..c91cafa10 100644
--- a/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplatCompressedData.js
+++ b/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplatCompressedData.js
@@ -38,27 +38,6 @@ fn unpackRotation(bits: u32) -> vec4f {
     return vec4f(a, b, c, m);
 }
 
-fn quatToMat3(R: vec4f) -> mat3x3f {
-    let qw_scalar = R.x;
-    let qx_vec = R.y;
-    let qy_vec = R.z;
-    let qz_vec = R.w;
-
-    return mat3x3f(
-        vec3f(1.0 - 2.0 * (qy_vec * qy_vec + qz_vec * qz_vec),
-              2.0 * (qx_vec * qy_vec + qw_scalar * qz_vec),
-              2.0 * (qx_vec * qz_vec - qw_scalar * qy_vec)),
-
-        vec3f(2.0 * (qx_vec * qy_vec - qw_scalar * qz_vec),
-              1.0 - 2.0 * (qx_vec * qx_vec + qz_vec * qz_vec),
-              2.0 * (qy_vec * qz_vec + qw_scalar * qx_vec)),
-
-        vec3f(2.0 * (qx_vec * qz_vec + qw_scalar * qy_vec),
-              2.0 * (qy_vec * qz_vec - qw_scalar * qx_vec),
-              1.0 - 2.0 * (qx_vec * qx_vec + qy_vec * qy_vec))
-    );
-}
-
 // read center
 fn readCenter(source: ptr<function, SplatSource>) -> vec3f {
     let tex_size_u = textureDimensions(chunkTexture, 0);
diff --git a/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplatData.js b/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplatData.js
index d95fe9f14..d6e08683e 100644
--- a/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplatData.js
+++ b/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplatData.js
@@ -13,34 +13,6 @@ fn readCenter(source: ptr<function, SplatSource>) -> vec3f {
     return bitcast<vec3f>(tA.xyz);
 }
 
-fn quatToMat3(R: vec4f) -> mat3x3f {
-    let x: f32 = R.w;
-    let y: f32 = R.x;
-    let z: f32 = R.y;
-    let w: f32 = R.z;
-
-    // pre-calculate squares and products
-    let xx: f32 = x * x; let yy: f32 = y * y; let zz: f32 = z * z; let ww: f32 = w * w;
-    let xy: f32 = x * y; let xz: f32 = x * z; let xw: f32 = x * w;
-    let yz: f32 = y * z; let yw: f32 = y * w; let zw: f32 = z * w;
-
-    return mat3x3f(
-        vec3f(
-            1.0 - 2.0 * (zz + ww),
-            2.0 * (yz + xw),
-            2.0 * (yw - xz)
-        ), vec3f(
-            2.0 * (yz - xw),
-            1.0 - 2.0 * (yy + ww),
-            2.0 * (zw + xy)
-        ), vec3f(
-            2.0 * (yw + xz),
-            2.0 * (zw - xy),
-            1.0 - 2.0 * (yy + zz)
-        )
-    );
-}
-
 fn unpackRotation(packed: vec3f) -> vec4f {
     return vec4f(packed.xyz, sqrt(max(0.0, 1.0 - dot(packed, packed))));
 }
@@ -49,7 +21,7 @@ fn unpackRotation(packed: vec3f) -> vec4f {
 fn readCovariance(source: ptr<function, SplatSource>, covA_ptr: ptr<function, vec3f>, covB_ptr: ptr<function, vec3f>) {
     let tB: vec4f = textureLoad(transformB, source.uv, 0);
 
-    let rot: mat3x3f = quatToMat3(unpackRotation(vec3f(unpack2x16float(bitcast<u32>(tAw)), tB.w)));
+    let rot: mat3x3f = quatToMat3(unpackRotation(vec3f(unpack2x16float(bitcast<u32>(tAw)), tB.w)).wxyz);
     let scale: vec3f = tB.xyz;
 
     // M = S * R
diff --git a/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplatSH.js b/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplatSH.js
index 36a295b82..98272602b 100644
--- a/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplatSH.js
+++ b/src/scene/shader-lib/chunks-wgsl/gsplat/vert/gsplatSH.js
@@ -7,31 +7,53 @@ fn unpack111011s(bits: u32) -> vec3f {
     return (vec3f((vec3<u32>(bits) >> vec3<u32>(21u, 11u, 0u)) & vec3<u32>(0x7ffu, 0x3ffu, 0x7ffu)) / vec3f(2047.0, 1023.0, 2047.0)) * 2.0 - 1.0;
 }
 
+struct ScaleAndSH {
+    scale: f32,
+    a: vec3f,
+    b: vec3f,
+    c: vec3f
+};
+
 // fetch quantized spherical harmonic coefficients
-fn fetchScale(t_in: vec4<u32>, scale: ptr<function, f32>, a: ptr<function, vec3f>, b: ptr<function, vec3f>, c: ptr<function, vec3f>) {
-    *scale = bitcast<f32>(t_in.x);
-    *a = unpack111011s(t_in.y);
-    *b = unpack111011s(t_in.z);
-    *c = unpack111011s(t_in.w);
+fn fetchScale(t_in: vec4<u32>) -> ScaleAndSH {
+    var result: ScaleAndSH;
+    result.scale = bitcast<f32>(t_in.x);
+    result.a = unpack111011s(t_in.y);
+    result.b = unpack111011s(t_in.z);
+    result.c = unpack111011s(t_in.w);
+    return result;
 }
 
+struct SH {
+    a: vec3f,
+    b: vec3f,
+    c: vec3f,
+    d: vec3f
+};
+
 // fetch quantized spherical harmonic coefficients
-fn fetch4(t_in: vec4<u32>, a: ptr<function, vec3f>, b: ptr<function, vec3f>, c: ptr<function, vec3f>, d: ptr<function, vec3f>) {
-    *a = unpack111011s(t_in.x);
-    *b = unpack111011s(t_in.y);
-    *c = unpack111011s(t_in.z);
-    *d = unpack111011s(t_in.w);
+fn fetch4(t_in: vec4<u32>) -> SH {
+    var result: SH;
+    result.a = unpack111011s(t_in.x);
+    result.b = unpack111011s(t_in.y);
+    result.c = unpack111011s(t_in.z);
+    result.d = unpack111011s(t_in.w);
+    return result;
 }
 
-fn fetch1(t_in: u32, a: ptr<function, vec3f>) {
-    *a = unpack111011s(t_in);
+fn fetch1(t_in: u32) -> vec3f {
+    return unpack111011s(t_in);
 }
 
 #if SH_BANDS == 1
     var splatSH_1to3: texture_2d<u32>;
 
     fn readSHData(source: ptr<function, SplatSource>, sh: ptr<function, array<vec3f, 3>>, scale: ptr<function, f32>) {
-        fetchScale(textureLoad(splatSH_1to3, source.uv, 0), scale, &sh[0], &sh[1], &sh[2]);
+        let result = fetchScale(textureLoad(splatSH_1to3, source.uv, 0));
+        *scale = result.scale;
+        sh[0] = result.a;
+        sh[1] = result.b;
+        sh[2] = result.c;
     }
 #elif SH_BANDS == 2
     var splatSH_1to3: texture_2d<u32>;
@@ -39,9 +61,19 @@ fn fetch1(t_in: u32, a: ptr<function, vec3f>) {
     var splatSH_8to11: texture_2d<u32>;
 
     fn readSHData(source: ptr<function, SplatSource>, sh: ptr<function, array<vec3f, 8>>, scale: ptr<function, f32>) {
-        fetchScale(textureLoad(splatSH_1to3, source.uv, 0), scale, &sh[0], &sh[1], &sh[2]);
-        fetch4(textureLoad(splatSH_4to7, source.uv, 0), &sh[3], &sh[4], &sh[5], &sh[6]);
-        fetch1(textureLoad(splatSH_8to11, source.uv, 0).x, &sh[7]);
+        let first: ScaleAndSH = fetchScale(textureLoad(splatSH_1to3, source.uv, 0));
+        *scale = first.scale;
+        sh[0] = first.a;
+        sh[1] = first.b;
+        sh[2] = first.c;
+
+        let second: SH = fetch4(textureLoad(splatSH_4to7, source.uv, 0));
+        sh[3] = second.a;
+        sh[4] = second.b;
+        sh[5] = second.c;
+        sh[6] = second.d;
+
+        sh[7] = fetch1(textureLoad(splatSH_8to11, source.uv, 0).x);
     }
 #else
     var splatSH_1to3: texture_2d<u32>;
@@ -50,10 +82,29 @@ fn fetch1(t_in: u32, a: ptr<function, vec3f>) {
     var splatSH_12to15: texture_2d<u32>;
 
     fn readSHData(source: ptr<function, SplatSource>, sh: ptr<function, array<vec3f, 15>>, scale: ptr<function, f32>) {
-        fetchScale(textureLoad(splatSH_1to3, source.uv, 0), scale, &sh[0], &sh[1], &sh[2]);
-        fetch4(textureLoad(splatSH_4to7, source.uv, 0), &sh[3], &sh[4], &sh[5], &sh[6]);
-        fetch4(textureLoad(splatSH_8to11, source.uv, 0), &sh[7], &sh[8], &sh[9], &sh[10]);
-        fetch4(textureLoad(splatSH_12to15, source.uv, 0), &sh[11], &sh[12], &sh[13], &sh[14]);
+        let first: ScaleAndSH = fetchScale(textureLoad(splatSH_1to3, source.uv, 0));
+        *scale = first.scale;
+        sh[0] = first.a;
+        sh[1] = first.b;
+        sh[2] = first.c;
+
+        let second: SH = fetch4(textureLoad(splatSH_4to7, source.uv, 0));
+        sh[3] = second.a;
+        sh[4] = second.b;
+        sh[5] = second.c;
+        sh[6] = second.d;
+
+        let third: SH = fetch4(textureLoad(splatSH_8to11, source.uv, 0));
+        sh[7] = third.a;
+        sh[8] = third.b;
+        sh[9] = third.c;
+        sh[10] = third.d;
+
+        let fourth: SH = fetch4(textureLoad(splatSH_12to15, source.uv, 0));
+        sh[11] = fourth.a;
+        sh[12] = fourth.b;
+        sh[13] = fourth.c;
+        sh[14] = fourth.d;
     }
 #endif
 
diff --git a/src/scene/shader-lib/chunks/gsplat/vert/gsplatSogsColor.js b/src/scene/shader-lib/chunks/gsplat/vert/gsplatSogsColor.js
deleted file mode 100644
index f610c52e6..000000000
--- a/src/scene/shader-lib/chunks/gsplat/vert/gsplatSogsColor.js
+++ /dev/null
@@ -1,19 +0,0 @@
-export default /* glsl */`
-uniform mediump sampler2D sh0;
-uniform mediump sampler2D opacities;
-
-uniform vec3 sh0_mins;
-uniform vec3 sh0_maxs;
-
-uniform float opacities_mins;
-uniform float opacities_maxs;
-
-float SH_C0 = 0.28209479177387814;
-
-vec4 readColor(in SplatSource source) {
-    vec3 clr = mix(sh0_mins, sh0_maxs, texelFetch(sh0, source.uv, 0).xyz);
-    float opacity = mix(opacities_mins, opacities_maxs, texelFetch(opacities, source.uv, 0).x);
-
-    return vec4(vec3(0.5) + clr * SH_C0, 1.0 / (1.0 + exp(opacity * -1.0)));
-}
-`;
diff --git a/src/scene/shader-lib/programs/lit-shader.js b/src/scene/shader-lib/programs/lit-shader.js
index fd3398b47..96ec72aef 100644
--- a/src/scene/shader-lib/programs/lit-shader.js
+++ b/src/scene/shader-lib/programs/lit-shader.js
@@ -14,10 +14,10 @@ import {
     cubemaProjectionNames, specularOcclusionNames, reflectionSrcNames, ambientSrcNames,
     REFLECTIONSRC_NONE
 } from '../../constants.js';
-import { shaderChunks } from '../chunks/chunks.js';
+import { shaderChunks } from '../chunks-glsl/chunks.js';
 import { ChunkUtils } from '../chunk-utils.js';
 import { ShaderPass } from '../../shader-pass.js';
-import { validateUserChunks } from '../chunks/chunk-validation.js';
+import { validateUserChunks } from '../chunks-glsl/chunk-validation.js';
 import { Debug } from '../../../core/debug.js';
 import { shaderChunksWGSL } from '../chunks-wgsl/chunks-wgsl.js';
 
diff --git a/src/scene/shader-lib/programs/particle.js b/src/scene/shader-lib/programs/particle.js
index f3d3b3c74..eff8fc673 100644
--- a/src/scene/shader-lib/programs/particle.js
+++ b/src/scene/shader-lib/programs/particle.js
@@ -2,7 +2,7 @@ import { SEMANTIC_POSITION, SEMANTIC_TEXCOORD0, SHADERLANGUAGE_GLSL, SHADERLANGU
 import { ShaderDefinitionUtils } from '../../../platform/graphics/shader-definition-utils.js';
 import { blendNames } from '../../constants.js';
 import { shaderChunksWGSL } from '../chunks-wgsl/chunks-wgsl.js';
-import { shaderChunks } from '../chunks/chunks.js';
+import { shaderChunks } from '../chunks-glsl/chunks.js';
 import { ShaderGenerator } from './shader-generator.js';
 
 const normalTypeNames = [
diff --git a/src/scene/shader-lib/programs/shader-generator-shader.js b/src/scene/shader-lib/programs/shader-generator-shader.js
index 95500277e..c23c308b1 100644
--- a/src/scene/shader-lib/programs/shader-generator-shader.js
+++ b/src/scene/shader-lib/programs/shader-generator-shader.js
@@ -2,7 +2,7 @@ import { hashCode } from '../../../core/hash.js';
 import { SEMANTIC_ATTR15, SEMANTIC_BLENDINDICES, SEMANTIC_BLENDWEIGHT, SHADERLANGUAGE_GLSL, SHADERLANGUAGE_WGSL } from '../../../platform/graphics/constants.js';
 import { ShaderDefinitionUtils } from '../../../platform/graphics/shader-definition-utils.js';
 import { shaderChunksWGSL } from '../chunks-wgsl/chunks-wgsl.js';
-import { shaderChunks } from '../chunks/chunks.js';
+import { shaderChunks } from '../chunks-glsl/chunks.js';
 import { ShaderGenerator } from './shader-generator.js';
 
 class ShaderGeneratorShader extends ShaderGenerator {
diff --git a/src/scene/shader-lib/shader-utils.js b/src/scene/shader-lib/shader-utils.js
new file mode 100644
index 000000000..1a0daa9dd
--- /dev/null
+++ b/src/scene/shader-lib/shader-utils.js
@@ -0,0 +1,191 @@
+import { Shader } from '../../platform/graphics/shader.js';
+import { ShaderDefinitionUtils } from '../../platform/graphics/shader-definition-utils.js';
+import { getProgramLibrary } from './get-program-library.js';
+import { Debug } from '../../core/debug.js';
+import { ShaderGenerator } from './programs/shader-generator.js';
+import { ShaderPass } from '../shader-pass.js';
+import { SHADERLANGUAGE_GLSL, SHADERLANGUAGE_WGSL } from '../../platform/graphics/constants.js';
+
+/**
+ * @import { GraphicsDevice } from '../../platform/graphics/graphics-device.js'
+ * @import { ShaderProcessorOptions } from '../../platform/graphics/shader-processor-options.js'
+ * @import { CameraShaderParams } from '../camera-shader-params.js'
+ * @import { Material, ShaderVariantParams } from '../materials/material.js'
+ */
+
+class ShaderGeneratorPassThrough extends ShaderGenerator {
+    constructor(key, shaderDefinition) {
+        super();
+        this.key = key;
+        this.shaderDefinition = shaderDefinition;
+    }
+
+    generateKey(options) {
+        return this.key;
+    }
+
+    createShaderDefinition(device, options) {
+        return this.shaderDefinition;
+    }
+}
+
+class ShaderUtils {
+    /**
+     * Creates a shader. When the active graphics device is WebGL, the provided GLSL vertex and
+     * fragment source code is used. For WebGPU, if WGSL vertex and fragment source code is
+     * supplied, it is used directly; otherwise, the system automatically translates the provided
+     * GLSL code into WGSL. In the case of GLSL shaders, additional blocks are appended to both the
+     * vertex and fragment source code to support extended features and maintain compatibility.
+     * These additions include the shader version declaration, precision qualifiers, and commonly
+     * used extensions, and therefore should be excluded from the user-supplied GLSL source.
+     *
+     * @param {GraphicsDevice} device - The graphics device.
+     * @param {object} options - Object for passing optional arguments.
+     * @param {string} options.uniqueName - Unique name for the shader. If a shader with this name
+     * already exists, it will be returned instead of a new shader instance.
+     * @param {Object<string, string>} options.attributes - Object detailing the mapping of vertex
+     * shader attribute names to semantics SEMANTIC_*. This enables the engine to match vertex
+     * buffer data to the shader attributes.
+     * @param {boolean} [options.useTransformFeedback] - Whether to use transform feedback. Defaults
+     * to false. Only supported by WebGL.
+     * @param {string} [options.vertexGLSL] - The vertex shader code in GLSL.
+     * @param {string} [options.vertexWGSL] - The vertex shader code in WGSL.
+     * @param {string} [options.fragmentGLSL] - The fragment shader code in GLSL.
+     * @param {string} [options.fragmentWGSL] - The fragment shader code in WGSL.
+     * @param {Map<string, string>} [options.vertexIncludes] - A map containing key-value pairs of
+     * include names and their content. These are used for resolving #include directives in the
+     * vertex shader source.
+     * @param {Map<string, string>} [options.vertexDefines] - A map containing key-value pairs of
+     * define names and their values. These are used for resolving #ifdef style of directives in the
+     * vertex code.
+     * @param {Map<string, string>} [options.fragmentIncludes] - A map containing key-value pairs
+     * of include names and their content. These are used for resolving #include directives in the
+     * fragment shader source.
+     * @param {Map<string, string>} [options.fragmentDefines] - A map containing key-value pairs of
+     * define names and their values. These are used for resolving #ifdef style of directives in the
+     * fragment code.
+     * @param {string | string[]} [options.fragmentOutputTypes] - Fragment shader output types,
+     * which default to vec4. Passing a string will set the output type for all color attachments.
+     * Passing an array will set the output type for each color attachment.
+     * @returns {Shader} The newly created shader.
+     */
+    static createShader(device, options) {
+
+        const programLibrary = getProgramLibrary(device);
+        let shader = programLibrary.getCachedShader(options.uniqueName);
+        if (!shader) {
+            const wgsl = device.isWebGPU && options.vertexWGSL && options.fragmentWGSL;
+            shader = new Shader(device, ShaderDefinitionUtils.createDefinition(device, {
+                name: options.uniqueName,
+                shaderLanguage: wgsl ? SHADERLANGUAGE_WGSL : SHADERLANGUAGE_GLSL,
+                attributes: options.attributes,
+                vertexCode: wgsl ? options.vertexWGSL : options.vertexGLSL,
+                fragmentCode: wgsl ? options.fragmentWGSL : options.fragmentGLSL,
+                useTransformFeedback: options.useTransformFeedback,
+                vertexIncludes: options.vertexIncludes,
+                vertexDefines: options.vertexDefines,
+                fragmentIncludes: options.fragmentIncludes,
+                fragmentDefines: options.fragmentDefines,
+                fragmentOutputTypes: options.fragmentOutputTypes
+            }));
+            programLibrary.setCachedShader(options.uniqueName, shader);
+        }
+        return shader;
+    }
+
+    /**
+     * Create a map of defines used for shader generation for a material.
+     *
+     * @param {Material} material - The material to create the shader defines for.
+     * @param {ShaderVariantParams} params - The shader variant parameters.
+     * @returns {Map<string, string>} The map of shader defines.
+     * @ignore
+     */
+    static getCoreDefines(material, params) {
+
+        // merge both maps, with camera shader params taking precedence
+        const defines = new Map(material.defines);
+        params.cameraShaderParams.defines.forEach((value, key) => defines.set(key, value));
+
+        // add pass defines
+        const shaderPassInfo = ShaderPass.get(params.device).getByIndex(params.pass);
+        shaderPassInfo.defines.forEach((value, key) => defines.set(key, value));
+
+        return defines;
+    }
+
+    /**
+     * Process shader using shader processing options, utilizing the cache of the ProgramLibrary.
+     *
+     * @param {Shader} shader - The shader to be processed.
+     * @param {ShaderProcessorOptions} processingOptions - The shader processing options.
+     * @returns {Shader} The processed shader.
+     * @ignore
+     */
+    static processShader(shader, processingOptions) {
+
+        Debug.assert(shader);
+        const shaderDefinition = shader.definition;
+
+        // 'shader' generator for a material - simply return existing shader definition. Use generator and getProgram
+        // to allow for shader processing to be cached
+        const name = shaderDefinition.name ?? 'shader';
+
+        // unique name based of the shader id
+        const key = `${name}-id-${shader.id}`;
+
+        const materialGenerator = new ShaderGeneratorPassThrough(key, shaderDefinition);
+
+        // temporarily register the program generator
+        const libraryModuleName = 'shader';
+        const library = getProgramLibrary(shader.device);
+        Debug.assert(!library.isRegistered(libraryModuleName));
+        library.register(libraryModuleName, materialGenerator);
+
+        // generate shader variant - its the same shader, but with different processing options
+        const variant = library.getProgram(libraryModuleName, {}, processingOptions);
+
+        // unregister it again
+        library.unregister(libraryModuleName);
+
+        return variant;
+    }
+}
+
+function createShader(device, vsName, fsName, useTransformFeedback = false, shaderDefinitionOptions = {}) {
+    Debug.removed('pc.createShader has been removed deprecated. Use ShaderUtils.createShader instead.');
+}
+
+function createShaderFromCode(device, vsCode, fsCode, uniqueName, attributes, useTransformFeedback = false, shaderDefinitionOptions = {}) {
+
+    Debug.deprecated('pc.createShaderFromCode has been deprecated. Use ShaderUtils.createShader instead.');
+
+    // the function signature has changed, fail if called incorrectly
+    Debug.assert(typeof attributes !== 'boolean');
+
+    // Normalize arguments to allow passing shaderDefinitionOptions as the 6th argument
+    if (typeof useTransformFeedback === 'boolean') {
+        shaderDefinitionOptions.useTransformFeedback = useTransformFeedback;
+    } else if (typeof useTransformFeedback === 'object') {
+        shaderDefinitionOptions = {
+            ...shaderDefinitionOptions,
+            ...useTransformFeedback
+        };
+    }
+
+    const programLibrary = getProgramLibrary(device);
+    let shader = programLibrary.getCachedShader(uniqueName);
+    if (!shader) {
+        shader = new Shader(device, ShaderDefinitionUtils.createDefinition(device, {
+            ...shaderDefinitionOptions,
+            name: uniqueName,
+            vertexCode: vsCode,
+            fragmentCode: fsCode,
+            attributes: attributes
+        }));
+        programLibrary.setCachedShader(uniqueName, shader);
+    }
+    return shader;
+}
+
+export { ShaderUtils, createShader, createShaderFromCode };
diff --git a/src/scene/shader-lib/utils.js b/src/scene/shader-lib/utils.js
deleted file mode 100644
index 80ba90344..000000000
--- a/src/scene/shader-lib/utils.js
+++ /dev/null
@@ -1,185 +0,0 @@
-import { Shader } from '../../platform/graphics/shader.js';
-import { ShaderDefinitionUtils } from '../../platform/graphics/shader-definition-utils.js';
-import { shaderChunks } from './chunks/chunks.js';
-import { getProgramLibrary } from './get-program-library.js';
-import { Debug } from '../../core/debug.js';
-import { ShaderGenerator } from './programs/shader-generator.js';
-import { ShaderPass } from '../shader-pass.js';
-
-/**
- * @import { GraphicsDevice } from '../../platform/graphics/graphics-device.js'
- * @import { ShaderProcessorOptions } from '../../platform/graphics/shader-processor-options.js'
- * @import { CameraShaderParams } from '../camera-shader-params.js'
- * @import { Material, ShaderVariantParams } from '../materials/material.js'
- */
-
-/**
- * Create a shader from named shader chunks.
- *
- * @param {GraphicsDevice} device - The graphics device.
- * @param {string} vsName - The vertex shader chunk name.
- * @param {string} fsName - The fragment shader chunk name.
- * @param {boolean | Record<string, boolean | string | string[]>} [useTransformFeedback] - Whether
- * to use transform feedback. Defaults to false.
- * @param {object} [shaderDefinitionOptions] - Additional options that will be added to the shader
- * definition.
- * @param {boolean} [shaderDefinitionOptions.useTransformFeedback] - Whether to use transform
- * feedback. Defaults to false.
- * @param {string | string[]} [shaderDefinitionOptions.fragmentOutputTypes] - Fragment shader
- * output types, which default to vec4. Passing a string will set the output type for all color
- * attachments. Passing an array will set the output type for each color attachment.
- * @see ShaderDefinitionUtils.createDefinition
- * @returns {Shader} The newly created shader.
- * @category Graphics
- */
-function createShader(device, vsName, fsName, useTransformFeedback = false, shaderDefinitionOptions = {}) {
-
-    // Normalize arguments to allow passing shaderDefinitionOptions as the 6th argument
-    if (typeof useTransformFeedback === 'boolean') {
-        shaderDefinitionOptions.useTransformFeedback = useTransformFeedback;
-    } else if (typeof useTransformFeedback === 'object') {
-        shaderDefinitionOptions = {
-            ...shaderDefinitionOptions,
-            ...useTransformFeedback
-        };
-    }
-
-    return new Shader(device, ShaderDefinitionUtils.createDefinition(device, {
-        ...shaderDefinitionOptions,
-        name: `${vsName}_${fsName}`,
-        vertexCode: shaderChunks[vsName],
-        fragmentCode: shaderChunks[fsName]
-    }));
-}
-
-/**
- * Create a shader from the supplied source code. Note that this function adds additional shader
- * blocks to both vertex and fragment shaders, which allow the shader to use more features and
- * compile on both WebGL and WebGPU. Specifically, these blocks are added, and should not be
- * part of provided vsCode and fsCode: shader version, shader precision, commonly used extensions.
- *
- * @param {GraphicsDevice} device - The graphics device.
- * @param {string} vsCode - The vertex shader code.
- * @param {string} fsCode - The fragment shader code.
- * @param {string} uniqueName - Unique name for the shader. If a shader with this name already
- * exists, it will be returned instead of a new shader instance.
- * @param {Object<string, string>} [attributes] - Object detailing the mapping of vertex shader
- * attribute names to semantics SEMANTIC_*. This enables the engine to match vertex buffer data as
- * inputs to the shader. Defaults to undefined, which generates the default attributes.
- * @param {boolean | Record<string, boolean | string | string[]>} [useTransformFeedback] - Whether
- * to use transform feedback. Defaults to false.
- * @param {object} [shaderDefinitionOptions] - Additional options that will be added to the shader
- * definition.
- * @param {boolean} [shaderDefinitionOptions.useTransformFeedback] - Whether to use transform
- * feedback. Defaults to false.
- * @param {string | string[]} [shaderDefinitionOptions.fragmentOutputTypes] - Fragment shader
- * output types, which default to vec4. Passing a string will set the output type for all color
- * attachments. Passing an array will set the output type for each color attachment.
- * @see ShaderDefinitionUtils.createDefinition
- * @returns {Shader} The newly created shader.
- * @category Graphics
- */
-function createShaderFromCode(device, vsCode, fsCode, uniqueName, attributes, useTransformFeedback = false, shaderDefinitionOptions = {}) {
-
-    // the function signature has changed, fail if called incorrectly
-    Debug.assert(typeof attributes !== 'boolean');
-
-    // Normalize arguments to allow passing shaderDefinitionOptions as the 6th argument
-    if (typeof useTransformFeedback === 'boolean') {
-        shaderDefinitionOptions.useTransformFeedback = useTransformFeedback;
-    } else if (typeof useTransformFeedback === 'object') {
-        shaderDefinitionOptions = {
-            ...shaderDefinitionOptions,
-            ...useTransformFeedback
-        };
-    }
-
-    const programLibrary = getProgramLibrary(device);
-    let shader = programLibrary.getCachedShader(uniqueName);
-    if (!shader) {
-        shader = new Shader(device, ShaderDefinitionUtils.createDefinition(device, {
-            ...shaderDefinitionOptions,
-            name: uniqueName,
-            vertexCode: vsCode,
-            fragmentCode: fsCode,
-            attributes: attributes
-        }));
-        programLibrary.setCachedShader(uniqueName, shader);
-    }
-    return shader;
-}
-
-class ShaderGeneratorPassThrough extends ShaderGenerator {
-    constructor(key, shaderDefinition) {
-        super();
-        this.key = key;
-        this.shaderDefinition = shaderDefinition;
-    }
-
-    generateKey(options) {
-        return this.key;
-    }
-
-    createShaderDefinition(device, options) {
-        return this.shaderDefinition;
-    }
-}
-
-/**
- * Process shader using shader processing options, utilizing cache of the ProgramLibrary
- *
- * @param {Shader} shader - The shader to be processed.
- * @param {ShaderProcessorOptions} processingOptions - The shader processing options.
- * @returns {Shader} The processed shader.
- */
-function processShader(shader, processingOptions) {
-
-    Debug.assert(shader);
-    const shaderDefinition = shader.definition;
-
-    // 'shader' generator for a material - simply return existing shader definition. Use generator and getProgram
-    // to allow for shader processing to be cached
-    const name = shaderDefinition.name ?? 'shader';
-
-    // unique name based of the shader id
-    const key = `${name}-id-${shader.id}`;
-
-    const materialGenerator = new ShaderGeneratorPassThrough(key, shaderDefinition);
-
-    // temporarily register the program generator
-    const libraryModuleName = 'shader';
-    const library = getProgramLibrary(shader.device);
-    Debug.assert(!library.isRegistered(libraryModuleName));
-    library.register(libraryModuleName, materialGenerator);
-
-    // generate shader variant - its the same shader, but with different processing options
-    const variant = library.getProgram(libraryModuleName, {}, processingOptions);
-
-    // unregister it again
-    library.unregister(libraryModuleName);
-
-    return variant;
-}
-
-/**
- * Create a map of defines used for shader generation for a material.
- *
- * @param {Material} material - The material to create the shader defines for.
- * @param {ShaderVariantParams} params - The shader variant parameters.
- * @returns {Map<string, string>} The map of shader defines.
- * @ignore
- */
-const getCoreDefines = (material, params) => {
-
-    // merge both maps, with camera shader params taking precedence
-    const defines = new Map(material.defines);
-    params.cameraShaderParams.defines.forEach((value, key) => defines.set(key, value));
-
-    // add pass defines
-    const shaderPassInfo = ShaderPass.get(params.device).getByIndex(params.pass);
-    shaderPassInfo.defines.forEach((value, key) => defines.set(key, value));
-
-    return defines;
-};
-
-export { createShader, createShaderFromCode, processShader, getCoreDefines };
diff --git a/src/scene/skybox/sky-mesh.js b/src/scene/skybox/sky-mesh.js
index dfc479b43..172caa7f5 100644
--- a/src/scene/skybox/sky-mesh.js
+++ b/src/scene/skybox/sky-mesh.js
@@ -4,7 +4,7 @@ import { ShaderMaterial } from '../materials/shader-material.js';
 import { MeshInstance } from '../mesh-instance.js';
 import { ChunkUtils } from '../shader-lib/chunk-utils.js';
 import { shaderChunksWGSL } from '../shader-lib/chunks-wgsl/chunks-wgsl.js';
-import { shaderChunks } from '../shader-lib/chunks/chunks.js';
+import { shaderChunks } from '../shader-lib/chunks-glsl/chunks.js';
 import { SkyGeometry } from './sky-geometry.js';
 
 /**


Test patch:
diff --git a/test/framework/components/element/text-element.test.mjs b/test/framework/components/element/text-element.test.mjs
index 09b35f4e3..5abacd837 100644
--- a/test/framework/components/element/text-element.test.mjs
+++ b/test/framework/components/element/text-element.test.mjs
@@ -1574,13 +1574,13 @@ describe('TextElement', function () {
     });
 
     it('text element removes i18n event listeners on destroy', function () {
-        expect(app.i18n.hasEvent('set:locale')).to.equal(true);
+        expect(app.i18n.hasEvent('change')).to.equal(true);
         expect(app.i18n.hasEvent('data:add')).to.equal(true);
         expect(app.i18n.hasEvent('data:remove')).to.equal(true);
 
         element.entity.destroy();
 
-        expect(app.i18n.hasEvent('set:locale')).to.equal(false);
+        expect(app.i18n.hasEvent('change')).to.equal(false);
         expect(app.i18n.hasEvent('data:add')).to.equal(false);
         expect(app.i18n.hasEvent('data:remove')).to.equal(false);
     });