Split out of #1555 / the #1556 tracker to record a design constraint found while implementing the WebGPU side, so it is not rediscovered later. Not scheduled — filed for the record, no work planned right now.
Where things stand
#1555 asks for a shared frame-globals uniform buffer (projection / tint / time) across all shaders. The WebGPU backend already ships that shape: frame globals live in a bind-group-0 uniform buffer, consumed by every pipeline. #1552 did the analogous thing for light data on both backends. The remaining half is WebGL.
The constraint: user shaders are contractually GLSL ES 1.00
GL uniform blocks require GLSL ES 3.00 (#version 300 es). melonJS user-authored shaders — ShaderEffect bodies and raw GLShader sources — are contractually ES 1.00, and we reaffirmed that guarantee in the 20.0 upgrade notes when the renderer went WebGL-2-only ("User shaders need no changes"). Converting the frame globals to a UBO across the board would force every user shader hosted by a built-in batcher to ES 3.00. That is a breaking change to the one part of the shader surface we promised not to break.
This is exactly why #1552 could move the four lit shaders to ES 3.00 without controversy: those are engine-owned, with no user-authored bodies hosted inside them. The same escape hatch does not exist for the quad and primitive families, which host user effect bodies by design.
The only viable shape, then
A built-ins-only uniform block: engine-owned shader families (tilemap, mesh, the lit variants — the ones already on ES 3.00 or free to move) read frame globals from a UBO, while user-hosting families keep ES 1.00 with individual uniforms. The cost is that the engine then maintains two forms of the same globals and keeps them in sync, which is real complexity for whoever touches that code next.
Expected win: modest — measure before building
Frame globals are not per-draw traffic today. Batcher.useShader() uploads the projection matrix once per shader-program switch, i.e. a handful of uniformMatrix4fv calls per frame in a scene that alternates batchers, and one in a scene that does not. A UBO would remove those and let a program switch skip re-uploading globals entirely — worth having, but nowhere near the #1552 win, which raised a capacity limit (8 → 32 lights) rather than shaving calls.
So the honest order of work, if this is ever picked up: measure the current per-frame uniform traffic first, and only build the dual-path complexity if the measurement justifies it.
Why file it at all
Without this note, the natural reading of #1555 is "the GL half is just leftover work" — it is not. It is blocked on a compatibility promise, and the workaround has a maintenance cost that should be a deliberate decision rather than a surprise mid-implementation.
Related: #1555, #1556, #1552.
🤖 Generated with Claude Code
https://claude.ai/code/session_01QVjYzf76AEU3wJk766JAQi
Split out of #1555 / the #1556 tracker to record a design constraint found while implementing the WebGPU side, so it is not rediscovered later. Not scheduled — filed for the record, no work planned right now.
Where things stand
#1555 asks for a shared frame-globals uniform buffer (projection / tint / time) across all shaders. The WebGPU backend already ships that shape: frame globals live in a bind-group-0 uniform buffer, consumed by every pipeline. #1552 did the analogous thing for light data on both backends. The remaining half is WebGL.
The constraint: user shaders are contractually GLSL ES 1.00
GL uniform blocks require GLSL ES 3.00 (
#version 300 es). melonJS user-authored shaders —ShaderEffectbodies and rawGLShadersources — are contractually ES 1.00, and we reaffirmed that guarantee in the 20.0 upgrade notes when the renderer went WebGL-2-only ("User shaders need no changes"). Converting the frame globals to a UBO across the board would force every user shader hosted by a built-in batcher to ES 3.00. That is a breaking change to the one part of the shader surface we promised not to break.This is exactly why #1552 could move the four lit shaders to ES 3.00 without controversy: those are engine-owned, with no user-authored bodies hosted inside them. The same escape hatch does not exist for the quad and primitive families, which host user effect bodies by design.
The only viable shape, then
A built-ins-only uniform block: engine-owned shader families (tilemap, mesh, the lit variants — the ones already on ES 3.00 or free to move) read frame globals from a UBO, while user-hosting families keep ES 1.00 with individual uniforms. The cost is that the engine then maintains two forms of the same globals and keeps them in sync, which is real complexity for whoever touches that code next.
Expected win: modest — measure before building
Frame globals are not per-draw traffic today.
Batcher.useShader()uploads the projection matrix once per shader-program switch, i.e. a handful ofuniformMatrix4fvcalls per frame in a scene that alternates batchers, and one in a scene that does not. A UBO would remove those and let a program switch skip re-uploading globals entirely — worth having, but nowhere near the #1552 win, which raised a capacity limit (8 → 32 lights) rather than shaving calls.So the honest order of work, if this is ever picked up: measure the current per-frame uniform traffic first, and only build the dual-path complexity if the measurement justifies it.
Why file it at all
Without this note, the natural reading of #1555 is "the GL half is just leftover work" — it is not. It is blocked on a compatibility promise, and the workaround has a maintenance cost that should be a deliberate decision rather than a surprise mid-implementation.
Related: #1555, #1556, #1552.
🤖 Generated with Claude Code
https://claude.ai/code/session_01QVjYzf76AEU3wJk766JAQi