gl_FragCoord

gl_FragCoord and Fragment-Space Coordinates

gl_FragCoord holds a fragment's window coordinates: x and y in drawing-buffer pixels from the bottom-left corner at pixel centers, z the depth from 0 to 1, and w as 1/w of clip space; GLH.evaluate() reports 0.5, 0.5, 0.5, 1 for its one pixel. For points, gl_PointCoord runs from 0 to 1 across the sprite, y pointing down:

A gl_FragCoord checkerboard behind a round point shaded by gl_PointCoordHTML
<canvas id="c" width="480" height="120" style="width:480px;height:120px"></canvas>
<script src="webgl-helper.js"></script>
<script>
const gl = GLH.paint(document.getElementById('c'), `#version 300 es
  precision mediump float; out vec4 c;
  void main() {
    vec2 cell = floor(gl_FragCoord.xy / 20.0);                // 20-pixel squares
    float shade = mod(cell.x + cell.y, 2.0) == 0.0 ? 0.93 : 0.85;
    c = vec4(vec3(shade) * (0.8 + 0.2 * gl_FragCoord.y / 120.0), 1.0);   // lighter upward
  }`);
GLH.load(gl, `#version 300 es
  void main() { gl_Position = vec4(0, 0, 0, 1); gl_PointSize = 100.0; }`, `#version 300 es
  precision mediump float; out vec4 c;
  void main() {
    if (distance(gl_PointCoord, vec2(0.5)) > 0.5) discard;   // round off the square
    c = vec4(gl_PointCoord, 0.4, 1.0);                         // red rightward, green downward
  }`);
gl.drawArrays(gl.POINTS, 0, 1);
window.__done = true;
</script>
Browser output of Listing 3.42
Browser output of 42

gl_FragCoord counts drawing-buffer pixels, not CSS pixels (scale by devicePixelRatio before comparing with the mouse), relative to the canvas, not the viewport.

A gl_FragCoord checkerboard behind a round point sprite shaded by gl_PointCoordHTMLLive
<!doctype html>
<style>
  body { margin: 0; background: #f7f4ee; }
  canvas { display: block; width: 100%; max-width: 480px; }
</style>
<canvas id="c" width="480" height="160"></canvas>
<script>
const gl = document.getElementById('c').getContext('webgl2');
function load(vsSource, fsSource) {
  const program = gl.createProgram();
  for (const [type, src] of [[gl.VERTEX_SHADER, vsSource], [gl.FRAGMENT_SHADER, fsSource]]) {
    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);
  return program;
}
gl.bindVertexArray(gl.createVertexArray());

// Background: every fragment of a full-canvas triangle, coloured by its window position.
load(`#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); }`,
`#version 300 es
  precision mediump float; out vec4 c;
  void main() {
    vec2 cell = floor(gl_FragCoord.xy / 20.0);                // 20-pixel squares from the bottom left
    float shade = mod(cell.x + cell.y, 2.0) == 0.0 ? 0.93 : 0.85;
    c = vec4(vec3(shade) * (0.8 + 0.2 * gl_FragCoord.y / 160.0), 1.0);   // lighter upward
  }`);
gl.drawArrays(gl.TRIANGLES, 0, 3);

// Points: gl_PointCoord runs 0..1 across each sprite, y pointing down.
const points = load(`#version 300 es
  uniform float uSize;
  void main() { gl_Position = vec4(-0.6 + 0.6 * float(gl_VertexID), 0.0, 0.0, 1.0); gl_PointSize = uSize; }`,
`#version 300 es
  precision mediump float; out vec4 c;
  void main() {
    if (distance(gl_PointCoord, vec2(0.5)) > 0.5) discard;   // round off the square
    c = vec4(gl_PointCoord, 0.4, 1.0);                         // red rightward, green downward
  }`);
gl.uniform1f(gl.getUniformLocation(points, 'uSize'), 110.0);
gl.drawArrays(gl.POINTS, 0, 3);
</script>