Webgl Shader Ogl
Minimum-viable animated WebGL noise/mesh-gradient shader background using OGL v1.0.…
$ prime install @community/template-webgl-shader-ogl Projection
Always in _index.xml · the agent never has to ask for this.
WebglShaderOgl [template] v1.0.0
Minimum-viable animated WebGL noise/mesh-gradient shader background using OGL v1.0.11 (~25KB, zero dependencies): a full-screen triangle with a fragment shader implementing 3-octave fractal Brownian motion (fbm) noise, DPR capped at 1.5 for GPU performance, reduced-motion guard, and CSS gradient fallback.
Loaded when retrieval picks the atom as adjacent / supporting.
WebglShaderOgl [template] v1.0.0
Minimum-viable animated WebGL noise/mesh-gradient shader background using OGL v1.0.11 (~25KB, zero dependencies): a full-screen triangle with a fragment shader implementing 3-octave fractal Brownian motion (fbm) noise, DPR capped at 1.5 for GPU performance, reduced-motion guard, and CSS gradient fallback.
Language
js
Body
// shader-bg.js — npm install ogl (v1.0.11)
import { Renderer, Program, Mesh, Triangle } from "ogl";
export function initShaderBg(container) {
// Skip for reduced-motion users — show CSS fallback
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return null;
const renderer = new Renderer({ dpr: Math.min(window.devicePixelRatio, 1.5) });
const gl = renderer.gl;
gl.clearColor(0.05, 0.05, 0.1, 1);
container.appendChild(gl.canvas);
Object.assign(gl.canvas.style, { position: "absolute", inset: "0", width: "100%", height: "100%" });
function resize() { renderer.setSize(container.offsetWidth, container.offsetHeight); }
window.addEventListener("resize", resize, { passive: true });
resize();
const geometry = new Triangle(gl); // one triangle covers full viewport (more efficient than quad)
const program = new Program(gl, {
vertex: /* glsl */ `
attribute vec2 uv; attribute vec2 position; varying vec2 vUv;
void main() { vUv = uv; gl_Position = vec4(position, 0.0, 1.0); }
`,
fragment: /* glsl */ `
precision highp float;
uniform float uTime; uniform vec2 uResolution; varying vec2 vUv;
float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
float noise(vec2 p) {
vec2 i = floor(p); vec2 f = fract(p);
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(mix(hash(i), hash(i + vec2(1,0)), u.x), mix(hash(i + vec2(0,1)), hash(i + vec2(1,1)), u.x), u.y);
}
float fbm(vec2 p) {
float v = 0.0; float amp = 0.5;
for (int i = 0; i < 3; i++) { v += amp * noise(p); p *= 2.0; amp *= 0.5; }
return v;
}
void main() {
float t = uTime * 0.12;
float n1 = fbm(vUv * 2.5 + vec2(t * 0.6, t * 0.4));
float n2 = fbm(vUv * 1.8 + vec2(-t * 0.3, t * 0.7) + n1);
vec3 colA = vec3(0.06, 0.04, 0.18); // deep indigo
vec3 colB = vec3(0.38, 0.12, 0.52); // violet
vec3 colC = vec3(0.72, 0.38, 0.08); // amber
vec3 color = mix(colA, colB, n1);
color = mix(color, colC, n2 * 0.4);
gl_FragColor = vec4(color, 1.0);
}
`,
uniforms: { uTime: { value: 0 }, uResolution: { value: [container.offsetWidth, container.offsetHeight] } },
});
const mesh = new Mesh(gl, { geometry, program });
let rafId;
function update(t) {
rafId = requestAnimationFrame(update);
program.uniforms.uTime.value = t * 0.001;
renderer.render({ scene: mesh });
}
rafId = requestAnimationFrame(update);
return function destroy() {
cancelAnimationFrame(rafId);
window.removeEventListener("resize", resize);
gl.canvas.remove();
renderer.gl.getExtension("WEBGL_lose_context")?.loseContext();
};
}
Usage Notes
- Decision ladder: static gradient (0KB) → CSS animated gradient (0KB) → OGL shader (25KB) → Three.js (200KB+). Use OGL only when you need organic noise movement.
- DPR cap at 1.5 is critical for mobile GPU performance — full Retina doubles the fragment shader work.
- 3 fbm octaves max: visually indistinguishable from 6 at this scale, runs twice as fast.
- aria-hidden='true' on the container — purely decorative, no information conveyed.
- Handle webglcontextlost and webglcontextrestored events in production for mobile memory-pressure recovery.
Css Fallback
/* Fallback shown when WebGL is skipped (reduced-motion or unavailable) */
.shader-bg-container {
background: linear-gradient(135deg,
oklch(0.15 0.1 270), oklch(0.35 0.15 290), oklch(0.55 0.12 40));
}
@media (prefers-reduced-motion: reduce) {
.shader-bg-container canvas { display: none; }
}
Loaded when retrieval picks the atom as a focal / direct hit.
WebglShaderOgl [template] v1.0.0
Minimum-viable animated WebGL noise/mesh-gradient shader background using OGL v1.0.11 (~25KB, zero dependencies): a full-screen triangle with a fragment shader implementing 3-octave fractal Brownian motion (fbm) noise, DPR capped at 1.5 for GPU performance, reduced-motion guard, and CSS gradient fallback.
Language
js
Body
// shader-bg.js — npm install ogl (v1.0.11)
import { Renderer, Program, Mesh, Triangle } from "ogl";
export function initShaderBg(container) {
// Skip for reduced-motion users — show CSS fallback
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return null;
const renderer = new Renderer({ dpr: Math.min(window.devicePixelRatio, 1.5) });
const gl = renderer.gl;
gl.clearColor(0.05, 0.05, 0.1, 1);
container.appendChild(gl.canvas);
Object.assign(gl.canvas.style, { position: "absolute", inset: "0", width: "100%", height: "100%" });
function resize() { renderer.setSize(container.offsetWidth, container.offsetHeight); }
window.addEventListener("resize", resize, { passive: true });
resize();
const geometry = new Triangle(gl); // one triangle covers full viewport (more efficient than quad)
const program = new Program(gl, {
vertex: /* glsl */ `
attribute vec2 uv; attribute vec2 position; varying vec2 vUv;
void main() { vUv = uv; gl_Position = vec4(position, 0.0, 1.0); }
`,
fragment: /* glsl */ `
precision highp float;
uniform float uTime; uniform vec2 uResolution; varying vec2 vUv;
float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
float noise(vec2 p) {
vec2 i = floor(p); vec2 f = fract(p);
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(mix(hash(i), hash(i + vec2(1,0)), u.x), mix(hash(i + vec2(0,1)), hash(i + vec2(1,1)), u.x), u.y);
}
float fbm(vec2 p) {
float v = 0.0; float amp = 0.5;
for (int i = 0; i < 3; i++) { v += amp * noise(p); p *= 2.0; amp *= 0.5; }
return v;
}
void main() {
float t = uTime * 0.12;
float n1 = fbm(vUv * 2.5 + vec2(t * 0.6, t * 0.4));
float n2 = fbm(vUv * 1.8 + vec2(-t * 0.3, t * 0.7) + n1);
vec3 colA = vec3(0.06, 0.04, 0.18); // deep indigo
vec3 colB = vec3(0.38, 0.12, 0.52); // violet
vec3 colC = vec3(0.72, 0.38, 0.08); // amber
vec3 color = mix(colA, colB, n1);
color = mix(color, colC, n2 * 0.4);
gl_FragColor = vec4(color, 1.0);
}
`,
uniforms: { uTime: { value: 0 }, uResolution: { value: [container.offsetWidth, container.offsetHeight] } },
});
const mesh = new Mesh(gl, { geometry, program });
let rafId;
function update(t) {
rafId = requestAnimationFrame(update);
program.uniforms.uTime.value = t * 0.001;
renderer.render({ scene: mesh });
}
rafId = requestAnimationFrame(update);
return function destroy() {
cancelAnimationFrame(rafId);
window.removeEventListener("resize", resize);
gl.canvas.remove();
renderer.gl.getExtension("WEBGL_lose_context")?.loseContext();
};
}
Usage Notes
- Decision ladder: static gradient (0KB) → CSS animated gradient (0KB) → OGL shader (25KB) → Three.js (200KB+). Use OGL only when you need organic noise movement.
- DPR cap at 1.5 is critical for mobile GPU performance — full Retina doubles the fragment shader work.
- 3 fbm octaves max: visually indistinguishable from 6 at this scale, runs twice as fast.
- aria-hidden='true' on the container — purely decorative, no information conveyed.
- Handle webglcontextlost and webglcontextrestored events in production for mobile memory-pressure recovery.
Css Fallback
/* Fallback shown when WebGL is skipped (reduced-motion or unavailable) */
.shader-bg-container {
background: linear-gradient(135deg,
oklch(0.15 0.1 270), oklch(0.35 0.15 290), oklch(0.55 0.12 40));
}
@media (prefers-reduced-motion: reduce) {
.shader-bg-container canvas { display: none; }
}
Source
prime-system/examples/frontend-design/primes/compiled/@community/template-webgl-shader-ogl/atom.yaml