Every shader has one void main() with no parameters; it reads globals and writes outputs such as gl_Position. Other functions use C syntax, must be declared (or prototyped) before use, may be overloaded on parameter types, can return structs and arrays, and cannot recurse. Names reserved for future use, such as half, input and filter, are rejected:
show('result = vec4(twice(2.0), twice(vec2(1.0, 2.0)), 0.0);',
'float twice(float x);\n' + // prototype
'vec2 twice(vec2 v) { return v * 2.0; }\n' + // overload for vec2
'float twice(float x) { return x * 2.0; }');
show('result = vec4(half(4.0));', 'float half(float x) { return x / 2.0; }');Output
4, 2, 4, 0 ERROR: 0:2: 'half' : Illegal use of reserved word
<!doctype html>
<style>
body { margin: 0; font: 12px system-ui, sans-serif; background: #f7f4ee; color: #333; }
canvas { display: block; width: 100%; max-width: 600px; }
pre { max-width: 600px; margin: 6px 0 0; padding: 6px 8px; background: #fff; border: 1px solid #ddd; font-size: 11px; white-space: pre-wrap; }
</style>
<canvas id="view" width="600" height="220"></canvas>
<pre id="out"></pre>
<script>
const gl = document.getElementById('view').getContext('webgl2');
const vs = `#version 300 es
void main() { gl_Position = vec4(gl_VertexID == 1 ? 3.0 : -1.0, gl_VertexID == 2 ? 3.0 : -1.0, 0.0, 1.0); }`;
const fs = `#version 300 es
precision mediump float;
out vec4 outColor;
struct Cover { vec3 color; float bands; };
float band(float y, float count); // prototype: defined after main()
vec3 tint(vec3 c) { return c * 1.15; } // overload 1
vec3 tint(vec3 c, float amount) { return c * amount; } // overload 2: same name, other parameters
Cover coverFor(int book) { // functions may return structs
const vec3 palette[3] = vec3[](vec3(0.12, 0.37, 0.55), vec3(0.88, 0.6, 0.06), vec3(0.71, 0.27, 0.18));
return Cover(palette[book % 3], float(2 + book));
}
void main() { // the one entry point: no parameters, no return value
vec2 p = gl_FragCoord.xy;
int book = int(p.x / 100.0);
vec2 local = vec2(mod(p.x, 100.0), p.y);
if (local.x < 15.0 || local.x > 85.0 || p.y < 20.0 || p.y > 200.0) {
outColor = vec4(0.97, 0.96, 0.93, 1.0);
return;
}
Cover cover = coverFor(book);
vec3 color = band(p.y / 180.0, cover.bands) > 0.5 ? tint(cover.color) : tint(cover.color, 0.7);
outColor = vec4(color, 1.0);
}
float band(float y, float count) { return step(0.5, fract(y * count)); }
`;
function compile(type, src) {
const s = gl.createShader(type);
gl.shaderSource(s, src); gl.compileShader(s);
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) throw new Error(gl.getShaderInfoLog(s));
return s;
}
const program = gl.createProgram();
gl.attachShader(program, compile(gl.VERTEX_SHADER, vs));
gl.attachShader(program, compile(gl.FRAGMENT_SHADER, fs));
gl.linkProgram(program);
gl.useProgram(program);
gl.bindVertexArray(gl.createVertexArray());
gl.drawArrays(gl.TRIANGLES, 0, 3);
// Names reserved for future use are rejected, even as ordinary function names.
try {
compile(gl.FRAGMENT_SHADER, `#version 300 es
precision mediump float; out vec4 o;
float half(float x) { return x / 2.0; }
void main() { o = vec4(half(1.0)); }`);
} catch (e) {
document.getElementById('out').textContent = 'float half(float x) { ... }\n-> ' + e.message.split('\n')[0];
}
</script>