Table of Contents

Fix, Fork, Contribute

WebGL2 What's New

WebGL2 is a pretty significant upgrade from WebGL1. If you're coming from WebGL1 and you want to know how to adjust your code so you can take advantage of WebGL2 see this article.

Here's the short list in no particular order.

Vertex Array Objects are always available

I think this is fairly important even though it was optionally available on WebGL1. Now that it's always available on WebGL2 I think you should probably always use them.

The size of a texture is available to shaders

In WebGL1 if your shader needed to know the size of a texture you had to pass the size in uniform manually. In WebGL2 you can call

vec2 size = textureSize(sampler, lod)

to get the size of any lod of a texture.

Direct Texel Lookup

It's often convenient to store large arrays of data in a texture. In WebGL 1 you could do that but you could only address textures with texture coordinates (0.0 to 1.0). In WebGL2 you can look up values from a texture by pixel/texel coordinates directly making array access slightly easier:

vec4 values = texelFetch(sampler, ivec2Position, lod);

Lots of texture formats

WebGL1 had just a few texture formats. WebGL2 has TONS!

  • RGBA32I
  • RGBA32UI
  • RGBA16I
  • RGBA16UI
  • RGBA8
  • RGBA8I
  • RGB10_A2
  • RGB10_A2UI
  • RGBA4
  • RGB5_A1
  • RGB8
  • RGB565
  • RG32I
  • RG32UI
  • RG16I
  • RG16UI
  • RG8
  • RG8I
  • RG8UI
  • R32I
  • R32UI
  • R16I
  • R16UI
  • R8
  • R8I
  • R8UI
  • RGBA32F
  • RGBA16F
  • RGB32F
  • RGB32I
  • RGB32UI
  • RGB16F
  • RGB16I
  • RGB16UI
  • RGB8I
  • RGB8UI
  • SRGB8
  • R11F_G11F_B10F
  • RGB9_E5
  • RG32F
  • RG16F
  • R32F
  • R16F
  • R8_SNORM

3D Textures

3D textures are just that, textures that have 3 dimensions.

Texture arrays

A texture array is very similar to a 3D texture except that each slice is considered a separate texture. All the slices have to be the same size, but this is a great way to give a shader access to hundreds of textures even though it only has a relatively small number of texture units. You can select the slice in your shader:

vec4 color = texture(someSampler2DArray, vec3(u, v, slice));

Non-Power of 2 Texture Support

In WebGL1 textures that were not a power of 2 could not have mips. In WebGL2 that limit is removed. Non-power of 2 textures work exactly the same as power of 2 textures.

Loop restrictions in shaders are removed

In WebGL1 a loop in a shader had to use a constant integer expression. WebGL2 removes that limit (in GLSL 300 es).

Matrix functions in GLSL

In WebGL1 if you needed to get the inverse of a matrix you had to pass it in as a uniform. In WebGL2 GLSL 300 es there's the built-in inverse function as well as transpose.

Common compressed textures

In WebGL1 there are various compressed texture formats that are hardware dependent. S3TC was basically desktop only. PVRTC was iOS only, etc.

In WebGL2 at least one suite of compressed texture formats are supported.

  • WEBGL_compressed_texture_etc AND/OR
  • (

    • WEBGL_compressed_texture_s3tc AND
    • WEBGL_compressed_texture_s3tc_srgb AND
    • EXT_texture_compression_rgtc


Uniform Buffer Objects

Uniform Buffer Objects let you specify a bunch of uniforms from a buffer. The advantages are:

  1. You can manipulate all the uniforms in the buffer outside of WebGL.

    In WebGL1 if you had 16 uniforms that would require 16 calls to gl.uniformXXX, that is relatively slow. In WebGL2 if you use a Uniform Buffer Object you can set the values in a typed array all inside JavaScript which means it's much much faster. When all the values are set you upload them all with 1 call to gl.bufferData or gl.bufferSubData and then tell the program to use that buffer with gl.bindBufferRange so only 2 calls.

  2. You can have different sets of uniform buffer objects.

    First some terms. A Uniform Block is a collection of uniforms defined in a shader. A Uniform Buffer Object is a buffer that contains the values a Uniform Block will use. You can create as many Uniform Buffer Objects as you want and bind one of them to a particular Uniform Block when you draw.

    For example, you could have 4 uniform blocks defined in a shader:

    • A global matrix uniform block that contains matrices that are the same for all draw calls like the projection matrix, view matrix, etc.

    • A per model uniform block that contains matrices that are different per model. For example, the world matrix and normal matrix.

    • A material uniform block that contains the material settings like diffuse, ambient, specular, etc.

    • A lighting uniform block that contains the lighting data like light color, light position, etc.

    Then at runtime you could create one global uniform buffer object, one model uniform buffer object per model, one light uniform buffer object per light, and one uniform buffer object per material.

    To draw any particular item, assuming all the values are already up to date, all you have to do is bind your desired 4 uniform buffer objects:

    gl.bindBufferRange(..., globalBlockIndx, globalMatrixUBO, ...);
    gl.bindBufferRange(..., modelBlockIndx, someModelMatrixUBO, ...);
    gl.bindBufferRange(..., materialBlockIndx, someMaterialSettingsUBO, ...);
    gl.bindBufferRange(..., lightBlockIndx, someLightSettingsUBO, ...);

Integer textures, attributes and math

In WebGL2 you can have integer based textures whereas in WebGL1 all textures represented floating point values even if they weren't represented by floating point values.

You can also have integer attributes.

On top of that, GLSL 300 es allows you to do bit manipulations of integers in the shaders.

Transform feedback

WebGL2 allows your vertex shader to write its results back to a buffer.


In WebGL1 all the texture parameters were per texture. In WebGL2 you can optionally use sampler objects. With samplers, all the filtering and repeat/clamping parameters that were part of a texture move to the sampler. This means a single texture can be sampled in different ways. Repeating or clamped. Filtered or not filtered.

Depth Textures

Depth textures were optional in WebGL1 and a PITA to work around. Now they're standard. They're commonly used for computing shadow maps.

Standard Derivatives

These are now standard. Common uses include computing normals in the shaders instead of passing them in.

Instanced Drawing

This is now standard. Common uses include drawing lots of trees, bushes, or grass quickly.


Being able to use 32bit ints for indices removes the size limit of indexed geometry.

Setting gl_FragDepth

You can write your own custom values to the depth buffer / z-buffer.

Blend Equation MIN / MAX

You are now able to take the min or max of 2 colors when blending.

Multiple Draw Buffers

You are now able to draw to multiple buffers at once from a shader. This is commonly used for various deferred rendering techniques.

Texture access in vertex shaders

In WebGL1 this was an optional feature. There was a count of how many textures you could access in a vertex shader, and that count was allowed to be 0. Most devices supported them. In WebGL2 that count is required to be at least 16.

Multi-Sampled renderbuffers

In WebGL1 the canvas itself could be anti-aliased with the GPU's built in multi-sample system, but there was no support for user controlled multi-sampling. In WebGL2 you can now make multi-sampled renderbuffers.

Occlusion Queries

Occlusion queries let you ask the GPU to check if pixels would actually get drawn if it were to render something.

Floating point textures are always available

Floating point textures are used for many special effects and calculations. In WebGL1 they were optional. In WebGL2 they just exist.

Note: Unfortunately they are still restricted in that filtering and rendering to float point textures is still optional. See OES_texture_float_linear and EXT_color_buffer_float.

Issue/Bug? Create an issue on github.
Use <pre><code>code goes here</code></pre> for code blocks
comments powered by Disqus