uniform*()

Sending Scalars and Vectors with uniform*()

Uniform setters are named uniform{1|2|3|4}{f|i|ui}[v]: the digit counts components, the letter is the component type (ui is WebGL 2 only), and a trailing v takes an array. The call must match the declaration: missing components default to 0, 0 and 1 only for vertexAttrib*(). Chrome 154 1 answers uniform4f() on a vec3, or uniform1f() on a sampler, with INVALID_OPERATION; a bool takes uniform1f() or uniform1i(); and a null location, from a misspelled name, is silently ignored:

uniform*() calls that match their declarationsJavaScript
const { program } = GLH.load(gl, '#version 300 es\nvoid main() { gl_Position = vec4(0); }',
  `#version 300 es\nprecision mediump float; uniform float uTime; uniform vec3 uTint;
  uniform sampler2D uCover; uniform uvec2 uSize; out vec4 c;
  void main() { c = texture(uCover, vec2(uSize)) * uTime + vec4(uTint, 1.0); }`);
const at = (name) => gl.getUniformLocation(program, name);
gl.uniform1f(at('uTime'), performance.now() / 1000);   // float
gl.uniform3fv(at('uTint'), [1.0, 0.9, 0.8]);            // vec3, or uniform3f(loc, 1, 0.9, 0.8)
gl.uniform1i(at('uCover'), 0);                          // sampler2D: texture unit 0
gl.uniform2ui(at('uSize'), 640, 480);                   // uvec2: WebGL 2 only
console.log('errors:', gl.getError());
Output
errors: 0

Values persist in the program until changed, so a uniform can differ from one draw call to the next.

uniform1f, uniform3fv, uniform1i and uniform2ui driving a cover, with getError() after each callHTMLLive
<!doctype html>
<style>
  body { margin: 0; font: 11px system-ui, sans-serif; background: #f7f4ee; color: #333; }
  .row { display: flex; gap: 10px; max-width: 600px; align-items: flex-start; }
  canvas { width: 180px; height: 240px; flex: none; }
  table { border-collapse: collapse; flex: 1; font-family: monospace; }
  td { padding: 4px 5px; border-bottom: 1px solid #e3ddd0; }
  .bad { color: #b5452f; }
</style>
<div class="row">
  <canvas id="view" width="180" height="240"></canvas>
  <table id="calls"></table>
</div>
<script>
const gl = document.getElementById('view').getContext('webgl2');
const program = gl.createProgram();
for (const [type, src] of [[gl.VERTEX_SHADER, `#version 300 es
in vec2 aPos; out vec2 vUV;
void main() { vUV = vec2(aPos.x, -aPos.y) * 0.5 + 0.5; gl_Position = vec4(aPos, 0.0, 1.0); }`],
  [gl.FRAGMENT_SHADER, `#version 300 es
precision mediump float;
uniform float uTime;        // uniform1f
uniform vec3 uTint;         // uniform3fv or uniform3f
uniform sampler2D uCover;   // uniform1i: a texture unit number
uniform uvec2 uStripes;     // uniform2ui: WebGL 2 only
uniform bool uGloss;        // uniform1i (or uniform1f)
in vec2 vUV; out vec4 outColor;
void main() {
  vec4 art = texture(uCover, vUV);
  uvec2 cell = uvec2(vUV * vec2(uStripes));
  float band = float((cell.x + cell.y) % 2u) * 0.08;
  vec3 c = art.rgb * uTint - band;
  if (uGloss) c += 0.35 * pow(max(0.0, 1.0 - abs(vUV.x + vUV.y - fract(uTime * 0.3) * 3.0) * 5.0), 2.0);
  outColor = vec4(c, 1.0);
}`]]) {
  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));
  gl.attachShader(program, s);
}
gl.linkProgram(program);
gl.useProgram(program);          // uniform*() always targets the current program

// Cover art from a 2D canvas on texture unit 0.
const art = document.createElement('canvas');
art.width = 128; art.height = 170;
const g = art.getContext('2d');
g.fillStyle = '#f2ead8'; g.fillRect(0, 0, 128, 170);
g.fillStyle = '#3f7d3a'; g.beginPath(); g.moveTo(0, 150); g.lineTo(60, 60); g.lineTo(128, 150); g.fill();
g.fillStyle = '#fff'; g.beginPath(); g.moveTo(48, 78); g.lineTo(60, 60); g.lineTo(72, 78); g.fill();
g.fillStyle = '#333'; g.font = 'bold 13px sans-serif'; g.textAlign = 'center';
g.fillText('Small Steps to', 64, 24); g.fillText('Big Summits', 64, 40);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, gl.createTexture());
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, art);
gl.generateMipmap(gl.TEXTURE_2D);

const at = (name) => gl.getUniformLocation(program, name);
const results = [];
function call(label, fn) { fn(); const e = gl.getError(); results.push([label, e]); }
call("uniform3fv(uTint, [1.0, 0.95, 0.85])", () => gl.uniform3fv(at('uTint'), [1.0, 0.95, 0.85]));
call("uniform1i(uCover, 0)", () => gl.uniform1i(at('uCover'), 0));
call("uniform2ui(uStripes, 6, 8)", () => gl.uniform2ui(at('uStripes'), 6, 8));
call("uniform1i(uGloss, 1)", () => gl.uniform1i(at('uGloss'), 1));
call("uniform4f(uTint, ...) on a vec3", () => gl.uniform4f(at('uTint'), 1, 1, 1, 1));
call("uniform1f(uCover, 0) on a sampler", () => gl.uniform1f(at('uCover'), 0));
call("uniform1f(uTimee, 1) misspelled", () => gl.uniform1f(at('uTimee'), 1));
document.getElementById('calls').innerHTML = results.map(([label, e]) =>
  `<tr><td>${label}</td><td class="${e ? 'bad' : ''}">${e === gl.INVALID_OPERATION ? 'INVALID_OPERATION' : e || 'NO_ERROR'}</td></tr>`).join('');

gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1, 1,-1, 1,1, -1,-1, 1,1, -1,1]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
const uTime = at('uTime');
function frame(ms) {
  gl.uniform1f(uTime, ms / 1000);   // values persist until changed: only the time changes per frame
  gl.drawArrays(gl.TRIANGLES, 0, 6);
  requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
</script>