Creating a Depth Texture

You need a place to store the map of depths nearest to the light source. That place is a special texture whose pixel format supports depth values rather than color tuples.

The following JavaScript allocates a blank depth texture using the same routines that you would use to create a color texture:

function reserveDepthTexture(width, height, unit = gl.TEXTURE0) {
  const texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, texture);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT32F, width, height, 0, gl.DEPTH_COMPONENT, gl.FLOAT, null);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  return texture;

The pixel format is gl.DEPTH_COMPONENT32F, which means the graphics card will store each texel as a 4-byte float. Normally you only get 1 byte of precision per texture channel. The greater precision will make your shadows smoother.

Sadly, WebGL does not support linear interpolation of depth textures. The filter parameters must be set to gl.NEAREST, which means the shadows will be pixelated. That's bad news.

Unlike the textures you've created in the past, you don't have any depth data to upload. So, the last parameter to texImage2D is null. WebGL allocates the space for the texture in VRAM but does not copy any data into it.