REGL API

May 29, 2021 ยท View on GitHub

Table of contents

Initialization

Quick start

As a fullscreen canvas

By default calling module.exports on the regl package creates a full screen canvas element and WebGLRenderingContext.

var regl = require('regl')()

This canvas will dynamically resize whenever the window changes shape. For most quick demos this is an easy way to get started using regl.

From a container div

Alternatively passing a container element as the first argument appends the generated canvas to its children.

var regl = require('regl')(element)

// or:

var regl = require('regl')({
  container: element
})

From a canvas

If the first argument is an HTMLCanvasElement, then regl will use this canvas to create a new WebGLRenderingContext that it renders into.

var regl = require('regl')(canvas)

// or:

var regl = require('regl')({
  canvas: canvas
})

From a WebGL context

Finally, if the first argument is a WebGLRenderingContext, then regl will just use this context without touching the DOM at all.

var regl = require('regl')(gl)

// or:

var regl = require('regl')({
  gl: gl
})

From a headless context

The above form can also be used to run regl headlessly by combining it with the headless-gl package. This works in node.js, electron and the browser.

//Creates a headless 256x256 regl instance
var regl = require('regl')(require('gl')(256, 256))

All initialization options

OptionsMeaning
glA reference to a WebGL rendering context. (Default created from canvas)
canvasA reference to an HTML canvas element. (Default created and appending to container)
containerA container element which regl inserts a canvas into. (Default document.body)
attributesThe context creation attributes passed to the WebGL context constructor. See below for defaults.
pixelRatioA multiplier which is used to scale the canvas size relative to the container. (Default window.devicePixelRatio)
extensionsA list of extensions that must be supported by WebGL context. Default []
optionalExtensionsA list of extensions which are loaded opportunistically. Default []
profileIf set, turns on profiling for all commands by default. (Default false)
onDoneAn optional callback which accepts a pair of arguments, (err, regl) that is called after the application loads. If not specified, context creation errors throw.

Notes

  • canvas or container may be a CSS selector string or a DOM element
  • extensions and optionalExtensions can be either arrays or comma separated strings representing all extensions. For more information see the WebGL extension registry
  • onDone is called

Error messages and debug mode

By default if you compile regl with browserify then all error messages and checks are removed. This is done in order to reduce the size of the final bundle and to improve run time performance.

If you want error messages and are using browserify make sure that you compile using --debug. Not only will this insert debug messages but it will also give you source maps which make finding errors easier.

Alternatively, consider using budo for your live development server. budo automatically compiles your code in debug mode and also provides facilities for live reloading.

Commands

Draw commands are the fundamental abstraction in regl. A draw command wraps up all of the WebGL state associated with a draw call (either drawArrays or drawElements) and packages it into a single reusable function. For example, here is a command that draws a triangle,

const drawTriangle = regl({
  frag: `
  void main() {
    gl_FragColor = vec4(1, 0, 0, 1);
  }`,

  vert: `
  attribute vec2 position;
  void main() {
    gl_Position = vec4(position, 0, 1);
  }`,

  attributes: {
    position: [[0, -1], [-1, 0], [1, 1]]
  },

  count: 3
})

To run a command you call it just like you would any function,

drawTriangle()

Executing commands

There are 3 ways to run a command,

One-shot rendering

In one shot rendering the command runs once immediately,

// Runs command immediately with no arguments
command()

// Runs a command using the specified arguments
command(props)

Batch rendering

A command can also run multiple times by passing a non-negative integer or an array as the first argument. The batchId is initially 0 and incremented for each iteration,

// Runs the command `count`-times
command(count)

// Runs the command once for each props
command([props0, props1, props2, ..., propsn])

In batch mode the command can be a little smarter regarding binding shaders/performing some checks. The command can then figure out which props are constant, which are dynamic, and then only apply the changes on each dynamic prop. This offers a small performance boost.

Scoped commands

Commands can be nested using scoping. If the argument to the command is a function then the command is evaluated and the state variables are saved as the defaults for all commands within its scope,

command(function (context) {
  // ... run sub commands
})

command(props, function (context) {
  // ... run sub commands
})

Inputs

Inputs to regl commands can come from one of three sources,

  • Context: Context variables are not used directly in commands, but can be passed into
  • Props: props are arguments which are passed into commands
  • this: this variables are indexed from the this variable that the command was called with

If you are familiar with Facebook's react, these are roughly analogous to a component's context, props and state variables respectively.

Example

var drawSpinningStretchyTriangle = regl({
  frag: `
  void main() {
    gl_FragColor = vec4(1, 0, 0, 1);
  }`,

  vert: `
  attribute vec2 position;
  uniform float angle, scale, width, height;
  void main() {
    float aspect = width / height;
    gl_Position = vec4(
      scale * (cos(angle) * position.x - sin(angle) * position.y),
      aspect * scale * (sin(angle) * position.x + cos(angle) * position.y),
      0,
      1.0);
  }`,

  attributes: {
    position: [[0, -1], [-1, 0], [1, 1]]
  },

  uniforms: {
    //
    // Dynamic properties can be functions.  Each function gets passed:
    //
    //  * context: which contains data about the current regl environment
    //  * props: which are user specified arguments
    //  * batchId: which is the index of the draw command in the batch
    //
    angle: function (context, props, batchId) {
      return props.speed * context.tick + 0.01 * batchId
    },

    // As a shortcut/optimization we can also just read out a property
    // from the props.  For example, this
    //
    scale: regl.prop('scale'),
    //
    // is semantically equivalent to
    //
    //  scale: function (context, props) {
    //    return props.scale
    //  }
    //

    // Similarly there are shortcuts for accessing context variables
    width: regl.context('viewportWidth'),
    height: regl.context('viewportHeight'),
    //
    // which is the same as writing:
    //
    // width: function (context) {
    //    return context.viewportWidth
    // }
    //
  },

  count: 3
})

To run a draw command with dynamic arguments we pass it a configuration object as the first argument,

// Draws one spinning triangle
drawSpinningStretchyTriangle({
  scale: 0.5,
  speed: 2
})

// Draws multiple spinning triangles
drawSpinningStretchyTriangle([
  { // batchId 0
    scale: 1,
    speed: 1,
  },
  { // batchId 1
    scale: 2,
    speed: 0.1,
  },
  { // batchId 2
    scale: 0.25,
    speed: 3
  }
])

Context

Context variables in regl are computed before any other parameters and can also be passed from a scoped command to any sub-commands. regl defines the following default context variables:

NameDescription
tickThe number of frames rendered
timeTotal time elapsed since the regl was initialized in seconds
viewportWidthWidth of the current viewport in pixels
viewportHeightHeight of the current viewport in pixels
framebufferWidthWidth of the current framebuffer in pixels
framebufferHeightHeight of the current framebuffer in pixels
drawingBufferWidthWidth of the WebGL context drawing buffer
drawingBufferHeightHeight of the WebGL context drawing buffer
pixelRatioThe pixel ratio of the drawing buffer

You can define context variables in the context block of a command. For example, here is how you can use context variables to set up a camera:

// This scoped command sets up the camera parameters
var setupCamera = regl({
  context: {
    projection: function (context) {
      return mat4.perspective([],
        Math.PI / 4,
        context.viewportWidth / context.viewportHeight,
        0.01,
        1000.0)
    },

    view: function (context, props) {
      return mat4.lookAt([],
        props.eye,
        props.target,
        [0, 1, 0])
    },

    eye: regl.props('eye')
  },

  uniforms: {
    view: regl.context('view'),
    invView: function (context) {
      return mat4.inverse([], context.view)
    },
    projection: regl.context('projection')
  }
})

// ... do stuff

// In the render function:
setupCamera({
  eye: [10, 0, 0],
  target: [0, 0, 0]
}, function () {

  // draw stuff
})

Drawing pure context with regl.draw() regl.draw() calls draw on an empty command it can be useful if you put everything for drawing into context variables basically regl.draw() is a shortcut for regl({})()

Props

The most common way to pass data into regl is via props. The props for a render command are declared

this

While regl strives to provide a stateless API, there are a few cases where it can be useful to cache state locally to a specific command. One way to achieve this is to use objects. When a regl command is run as a member function of an object, the this parameter is set to the object on which it was called and is passed to all computed parameters. For example, this shows how to use regl to create a simple reusable mesh object,

// First we create a constructor
function Mesh (center, {positions, cells}) {
  this.center = center
  this.positions = regl.buffer(positions)
  this.cells = regl.buffer(cells)
}

// Then we assign regl commands directly to the prototype of the class
Mesh.prototype.draw = regl({
  vert: `
  uniform mat4 projection, view, model;
  attribute vec3 position;
  void main () {
    gl_Position = projection * view * model * vec4(position, 1);
  }`,

  frag: `
  void main () {
    gl_FragColor = vec4(1, 0, 0, 1);
  }`,

  uniforms: {
    // dynamic properties are invoked with the same `this` as the command
    model: function () => {
      var c = this.center
      return [
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        -c[0], -c[1], -c[2], 1
      ]
    },

    view: regl.prop('view'),
    projection: regl.prop('projection')
  }

  attributes: {
    // here we are using 'positions' property of the mesh
    position: regl.this('positions')
  },

  // and same for the cells
  elements: regl.this('cells')
})

Once defined, we could then use these mesh objects as follows:

// Initialize meshes
var bunnyMesh = new Mesh([5, 2, 1], require('bunny'))
var teapotMesh = new Mesh([0, -3, 0], require('teapot'))

// ... set up rest of scene, compute matrices etc.
var view, projection

// Now draw meshes:
bunnyMesh.draw({
  view: view,
  projection: projection
})

teapotMesh.draw({
  view: view,
  projection: projection
})

Parameters

The input to a command declaration is a complete description of the WebGL state machine in the form of an object. The properties of this object are parameters which specify how values in the WebGL state machine are to be computed.


Shaders

Each draw command can specify the source code for a vertex and/or fragment shader,

var command = regl({
  // ...

  vert: `
  void main() {
    gl_Position = vec4(0, 0, 0, 1);
  }`,

  frag: `
  void main() {
    gl_FragColor = vec4(1, 0, 1, 1);
  }`,

  // ...
})
PropertyDescription
vertSource code of vertex shader
fragSource code of fragment shader

Related WebGL APIs


Uniforms

Uniform variables are specified in the uniforms block of the command. For example,

var command = regl({
  // ...

  vert: `
  struct SomeStruct {
    float value;
  };

  uniform vec4 someUniform;
  uniform int anotherUniform;
  uniform SomeStruct nested;
  uniform vec4 colors[2];

  void main() {
    gl_Position = vec4(1, 0, 0, 1);
  }`,

  uniforms: {
    someUniform: [1, 0, 0, 1],
    anotherUniform: regl.prop('myProp'),
    'nested.value': 5.3,
    'colors[0]': [0, 1, 0, 1],
    'colors[1]': [0, 0, 1, 1]
  },

  // ...
})

Notes

  • To specify uniforms in nested structs use the fully qualified path with dot notation
  • Matrix uniforms are specified as flat length n^2 arrays without transposing

Related WebGL APIs


Attributes

var command = regl({
  // ...

  attributes: {
    // Attributes can be declared explicitly
    normals: {
      buffer: regl.buffer([
        // ...
      ]),
      offset: 0,
      stride: 12,
      normalized: false,

      // divisor is only used if instancing is enabled
      divisor: 0
    },

    // A regl.buffer or the arguments to regl.buffer may also be specified
    position: [
      0, 1, 2,
      2, 3, 4,
      ...
    ],

    // Finally, attributes may be initialized with a constant value
    color: {
      constant: [1, 0, 1, 1]
    }
  },

  // ...
})

Each attribute can have any of the following optional properties,

PropertyDescriptionDefault
bufferA REGLBuffer wrapping the buffer objectnull
offsetThe offset of the vertexAttribPointer in bytes0
strideThe stride of the vertexAttribPointer in bytes0
normalizedWhether the pointer is normalizedfalse
sizeThe size of the vertex attributeInferred from shader
divisorSets gl.vertexAttribDivisorANGLE0 *

Notes

  • Attribute size is inferred from the shader vertex attribute if not specified
  • If a buffer is passed for an attribute then all pointer info is inferred
  • If the arguments to regl.buffer are passed, then a buffer is constructed
  • If an array is passed to an attribute, then the vertex attribute is set to a constant
  • divisor is only supported if the ANGLE_instanced_arrays extension is available

Related WebGL APIs


Drawing

var command = regl({
  // ...

  primitive: 'triangles',
  offset: 0,
  count: 6
})
PropertyDescriptionDefault
primitiveSets the primitive type'triangles' *
countNumber of vertices to draw0 *
offsetOffset of primitives to draw0
instancesNumber of instances to render0 **
elementsElement array buffernull

Notes

  • If elements is specified while primitive, count and offset are not, then these values may be inferred from the state of the element array buffer.
  • elements must be either an instance of regl.elements or else the arguments to regl.elements
  • instances is only applicable if the ANGLE_instanced_arrays extension is present.
  • primitive can take on the following values
Primitive typeDescription
'points'gl.POINTS
'lines'gl.LINES
'line strip'gl.LINE_STRIP
'line loopgl.LINE_LOOP
'trianglesgl.TRIANGLES
'triangle strip'gl.TRIANGLE_STRIP
'triangle fan'gl.TRIANGLE_FAN

Related WebGL APIs


Render target

A regl.framebuffer object may also be specified to allow for rendering to offscreen locations.

var command = regl({
  framebuffer: fbo
})

Notes

  • framebuffer must be a regl.framebuffer object
  • Passing null sets the framebuffer to the drawing buffer
  • Updating the render target will modify the viewport

Related WebGL APIs


Profiling

regl can optionally instrument commands to track profiling data. This is toggled by setting the profile flag on each command.

var myScope = regl({
  profile: true
})

var drawA = regl({ ... })
var drawB = regl({ ... })

regl.frame(function () {
  myScope(function () {
    drawA()
    drawB()
  })

  console.log(drawA.stats.count)
  console.log(drawB.stats.count)
})

The following stats are tracked for each command in the .stats property:

StatisticMeaning
countThe number of times the command has been called
cpuTimeThe cumulative CPU time spent executing the command in milliseconds
gpuTimeThe cumulative GPU time spent executing the command in milliseconds (requires the EXT_disjoint_timer_query extension)

Notes

  • GPU timer queries update asynchronously. If you are not using regl.frame() to tick your application, then you should periodically call regl.poll() each frame to update the timer statistics.
  • CPU time uses performance.now if available, otherwise it falls back to Date.now

Related WebGL APIs


Depth buffer

All state relating to the depth buffer is stored in the depth field of the command. For example,

var command = regl({
  // ...

  depth: {
    enable: true,
    mask: true,
    func: 'less',
    range: [0, 1]
  },

  // ..
})
PropertyDescriptionDefault
enableToggles gl.enable(gl.DEPTH_TEST)true
maskSets gl.depthMasktrue
rangeSets gl.depthRange[0, 1]
funcSets gl.depthFunc. See table below for possible values'less'

Notes

  • depth.func can take on the possible values
ValueDescription
'never'gl.NEVER
'always'gl.ALWAYS
'<', 'less'gl.LESS
'<=', 'lequal'gl.LEQUAL
'>', 'greater'gl.GREATER
'>=', 'gequal'gl.GEQUAL
'=', 'equal'gl.EQUAL
'!=', 'notequal'gl.NOTEQUAL

Related WebGL APIs


Blending

Blending information is stored in the blend field,

var command = regl({
  // ...

  blend: {
    enable: true,
    func: {
      srcRGB: 'src alpha',
      srcAlpha: 1,
      dstRGB: 'one minus src alpha',
      dstAlpha: 1
    },
    equation: {
      rgb: 'add',
      alpha: 'add'
    },
    color: [0, 0, 0, 0]
  },

  // ...
})
PropertyDescriptionDefault
enableToggles gl.enable(gl.BLEND)false
equationSets gl.blendEquation (see table)'add'
funcSets gl.blendFunc (see table){src:'src alpha',dst:'one minus src alpha'}
colorSets gl.blendColor[0, 0, 0, 0]

Notes

  • equation can be either a string or an object with the fields {rgb, alpha}. The former corresponds to gl.blendEquation and the latter to gl.blendEquationSeparate
  • The fields of equation can take on the following values
EquationDescription
'add'gl.FUNC_ADD
'subtract'gl.FUNC_SUBTRACT
'reverse subtract'gl.FUNC_REVERSE_SUBTRACT
'min'gl.MIN_EXT
'max'gl.MAX_EXT
  • 'min' and 'max' are only available if the EXT_blend_minmax extension is supported
  • func can be an object with the fields {src, dst} or {srcRGB, srcAlpha, dstRGB, dstAlpha}, with the former corresponding to gl.blendFunc and the latter to gl.blendFuncSeparate
  • The fields of func can take on the following values
FuncDescription
0, 'zero'gl.ZERO
1, 'one'gl.ONE
'src color'gl.SRC_COLOR
'one minus src color'gl.ONE_MINUS_SRC_COLOR
'src alpha'gl.SRC_ALPHA
'one minus src alpha'gl.ONE_MINUS_SRC_ALPHA
'dst color'gl.DST_COLOR
'one minus dst color'gl.ONE_MINUS_DST_COLOR
'dst alpha'gl.DST_ALPHA
'one minus dst alpha'gl.ONE_MINUS_DST_ALPHA
'constant color'gl.CONSTANT_COLOR
'one minus constant color'gl.ONE_MINUS_CONSTANT_COLOR
'constant alpha'gl.CONSTANT_ALPHA
'one minus constant alpha'gl.ONE_MINUS_CONSTANT_ALPHA
'src alpha saturate'gl.SRC_ALPHA_SATURATE

Related WebGL APIs


Stencil

Example:

var command = regl({
  // ...

  stencil: {
    enable: true,
    mask: 0xff,
    func: {
      cmp: '<',
      ref: 0,
      mask: 0xff
    },
    opFront: {
      fail: 'keep',
      zfail: 'keep',
      zpass: 'keep'
    },
    opBack: {
      fail: 'keep',
      zfail: 'keep',
      zpass: 'keep'
    }
  },

  // ...
})
PropertyDescriptionDefault
enableToggles gl.enable(gl.STENCIL_TEST)false
maskSets gl.stencilMask-1
funcSets gl.stencilFunc{cmp:'always',ref:0,mask:-1}
opFrontSets gl.stencilOpSeparate for front face{fail:'keep',zfail:'keep',zpass:'keep'}
opBackSets gl.stencilOpSeparate for back face{fail:'keep',zfail:'keep',zpass:'keep'}
opSets opFront and opBack simultaneously

Notes

  • func is an object which configures the stencil test function. It has 3 properties,

    • cmp which is the comparison function
    • ref which is the reference value
    • mask which is the comparison mask
  • func.cmp is a comparison operator which takes one of the following values,

ValueDescription
'never'gl.NEVER
'always'gl.ALWAYS
'<', 'less'gl.LESS
'<=', 'lequal'gl.LEQUAL
'>', 'greater'gl.GREATER
'>=', 'gequal'gl.GEQUAL
'=', 'equal'gl.EQUAL
'!=', 'notequal'gl.NOTEQUAL
  • opFront and opBack specify the stencil op. Each is an object which takes the following parameters:

    • fail, the stencil op which is applied when the stencil test fails
    • zfail, the stencil op which is applied when the stencil test passes and the depth test fails
    • zpass, the stencil op which is applied when both stencil and depth tests pass
  • Values for op.fail, op.zfail, op.zpass can come from the following table

Stencil OpDescription
'zero'gl.ZERO
'keep'gl.KEEP
'replace'gl.REPLACE
'invert'gl.INVERT
'increment'gl.INCR
'decrement'gl.DECR
'increment wrap'gl.INCR_WRAP
'decrement wrap'gl.DECR_WRAP

Related WebGL APIs


Polygon offset

Polygon offsetting behavior can be controlled using the polygonOffset field,

var command = regl({
  // ...

  polygonOffset: {
    enable: true,
    offset: {
      factor: 1,
      units: 0
    }
  }

  // ...
})
PropertyDescriptionDefault
enableToggles gl.enable(gl.POLYGON_OFFSET_FILL)false
offsetSets gl.polygonOffset{factor:0, units:0}

Related WebGL APIs


Culling

Example,

var command = regl({
  // ...

  cull: {
    enable: true,
    face: 'back'
  },

  // ...
})
PropertyDescriptionDefault
enableToggles gl.enable(gl.CULL_FACE)false
faceSets gl.cullFace'back'

Notes

  • face must be one of the following values,
FaceDescription
'front'gl.FRONT
'back'gl.BACK

Relevant WebGL APIs


Front face

Example,

var command = regl({
  // ...

  frontFace: 'cw',

  // ...
})
PropertyDescriptionDefault
frontFaceSets gl.frontFace'ccw'

Notes

  • The value for front face must be one of the following,
OrientationDescription
'cw'gl.CW
'ccw'gl.CCW

Relevant WebGL APIs


Dithering

Example,

var command = regl({
  // ...

  dither: true,

  // ...
})
PropertyDescriptionDefault
ditherToggles gl.DITHERfalse

Line width

Example,

var command = regl({
  // ...

  lineWidth: 4,

  // ...
})
PropertyDescriptionDefault
lineWidthSets gl.lineWidth1

Relevant WebGL APIs


Color mask

Example,

var command = regl({
  // ...

  colorMask: [true, false, true, false],

  // ...
})
PropertyDescriptionDefault
colorMaskSets gl.colorMask[true, true, true, true]

Relevant WebGL APIs


Sample coverage

Example,

var command = regl({
  // ...

  sample: {
    enable: true,
    alpha: false,
    coverage: {
      value: 1,
      invert: false
    }
  },

  // ...
})
PropertyDescriptionDefault
enableToggles gl.enable(gl.SAMPLE_COVERAGE)false
alphaToggles gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE)false
coverageSets gl.sampleCoverage{value:1,invert:false}

Relevant WebGL APIs


Scissor

Example,

var command = regl({
  // ...

  scissor: {
    enable: true,
    box: {
      x: 10,
      y: 20,
      width: 100,
      height: 100
    }
  }

  // ...
})
PropertyDescriptionDefault
enableToggles gl.enable(gl.SCISSOR)false
boxSets gl.scissor{}

Notes

  • box is the shape of the scissor region, it takes the following parameters

    • x is the left coordinate of the box, default 0
    • y is the top coordiante of the box, default 0
    • width is the width of the box, default fbo width - x
    • height is the height of the box, default fbo height - y

Relevant WebGL APIs


Viewport

Example,

var command = regl({
  // ...

  viewport: {
    x: 5,
    y: 10,
    width: 100,
    height: 50
  }

  // ...
})
PropertyDescriptionDefault
viewportThe shape of viewport{}

Notes

  • Like scissor.box, viewport is a bounding box with properties x,y,w,h
  • Updating viewport will modify the context variables viewportWidth and viewportHeight

Relevant WebGL APIs


Resources

Besides commands, the other major component of regl are resources. Resources are GPU resident objects which are managed explicitly by the programmer. Each resource follows a the same life cycle of create/read/update/delete.


Buffers

regl.buffer wraps WebGL array buffer objects.

Buffer constructor

// Creates an empty length 100 buffer
var zeroBuffer = regl.buffer(100)

// A buffer with float data
var floatBuffer = regl.buffer(new Float32Array([1, 2, 3, 4]))

// A streaming buffer of bytes
var streamBuffer = regl.buffer({
  usage: 'stream',
  data: new Uint8Array([2, 4, 6, 8, 10])
})

// An unpacked buffer of position data
var positionBuffer = regl.buffer([
  [1, 2, 3],
  [4, 5, 6],
  [2, 1, -2]
])
PropertyDescriptionDefault
dataThe data for the vertex buffer (see below)null
lengthIf data is null or not present reserves space for the buffer0
usageSets array buffer usage hint'static'
typeData type for vertex buffer'uint8'
  • usage can be one of the following values
Usage HintDescription
'static'gl.DRAW_STATIC
'dynamic'gl.DYNAMIC_DRAW
'stream'gl.STREAM_DRAW
  • type can be one of the following data types
Data typeDescription
'uint8'gl.UNSIGNED_BYTE
'int8'gl.BYTE
'uint16'gl.UNSIGNED_SHORT
'int16'gl.SHORT
'uint32'gl.UNSIGNED_INT
'int32'gl.INT
'float32', 'float'gl.FLOAT

Relevant WebGL APIs

Buffer update

To reinitialize a buffer in place, we can call the buffer as a function:

// First we create a buffer
var myBuffer = regl.buffer(5)

// ... do stuff ...

// Now reinitialize myBuffer
myBuffer({
  data: [
    1, 2, 3, 4, 5
  ]
})

The arguments to the update pathway are the same as the constructor and the returned value will be a reference to the buffer.

Relevant WebGL APIs

Buffer subdata

For performance reasons we may sometimes want to update just a portion of the buffer. You can update a portion of the buffer using the subdata method. This can be useful if you are dealing with frequently changing or streaming vertex data. Here is an example:

// First we preallocate a buffer with 100 bytes of data
var myBuffer = regl.buffer({
  usage: 'dynamic',  // give the WebGL driver a hint that this buffer may change
  type: 'float',
  length: 100
})

// Now we initialize the head of the buffer with the following data
myBuffer.subdata([ 0, 1, 2, 3, 4, 5 ])
//
// untyped arrays and arrays-of-arrays are converted to the same data type as
// the buffer.  typedarrays are copied bit-for-bit into the buffer
// with no type conversion.
//

// We can also update the buffer at some byte offset by passing this as
// the second argument to subdata
myBuffer.subdata([[7, 8], [9, 10]], 8)
//
// now the contents of myBuffer are:
//
//  new Float32Array([0, 1, 7, 8, 9, 10, 0, 0, 0, .... ])
//

Relevant WebGL APIs

Buffer destructor

Calling .destroy() on a buffer releases all resources associated to the buffer:

// Create a buffer
var myBuffer = regl.buffer(10)

// destroys the buffer
myBuffer.destroy()

Profiling info

The following stats are tracked for each buffer in the .stats property:

StatisticMeaning
sizeThe size of the buffer in bytes

Elements

regl.elements wraps WebGL element array buffer objects. Each regl.elements object stores a buffer object as well as the primitive type and vertex count.

Element constructor

var triElements = regl.elements([
  [1, 2, 3],
  [0, 2, 5]
])

var starElements = regl.elements({
  primitive: 'line loop',
  count: 5,
  data: new Uint8Array([0, 2, 4, 1, 3])
})
PropertyDescriptionDefault
dataThe data of the element buffernull
usageUsage hint (see gl.bufferData)'static'
lengthLength of the element buffer in bytes0 *
primitiveDefault primitive type for element buffer'triangles' *
typeData type for element buffer'uint8'
countVertex count for element buffer0 *
  • usage must take on one of the following values
Usage HintDescription
'static'gl.DRAW_STATIC
'dynamic'gl.DYNAMIC_DRAW
'stream'gl.STREAM_DRAW
  • primitive can be one of the following primitive types
Primitive typeDescription
'points'gl.POINTS
'lines'gl.LINES
'line strip'gl.LINE_STRIP
'line loop'gl.LINE_LOOP
'triangles'gl.TRIANGLES
'triangle strip'gl.TRIANGLE_STRIP
'triangle fan'gl.TRIANGLE_FAN
  • type can be one of the following data types
Data typeDescriptionExtension?
'uint8'gl.UNSIGNED_BYTE
'uint16'gl.UNSIGNED_SHORT
'uint32'gl.UNSIGNED_INTOES_element_index_uint

Notes

  • primitive, count and length are inferred from the vertex data

Relevant WebGL APIs

Element update

As in the case of buffers, calling an element buffer as a function reinitializes an element buffer in place. The arguments are the same as for the constructor. For example:

// First we create an element buffer
var myElements = regl.elements()

// Then we update it by calling it directly
myElements({
  data: [
    [1, 2, 3],
    [0, 2, 1]
  ]
})

Relevant WebGL APIs

Element subdata

Again like buffers it is possible to preallocate an element buffer and update regions of the elements using the subdata command.

// First we preallocate the element buffer
var myElements = regl.elements({
  primitive: 'triangles',
  usage: 'dynamic',
  type: 'uint16',
  length: 4096,
  count: 0
})

// Then we can update into ranges of the element buffer using subdata
myElements.subdata(
  [ [0, 1, 2],
    [2, 1, 3] ])

Relevant WebGL APIs

Element destructor

// First we create an element buffer
var myElements = regl.elements({ ... })

// Calling .destroy() on an element buffer releases all resources associated to
// it
myElements.destroy()

Relevant WebGL APIs


Textures

Texture constructor

There are many ways to upload data to a texture in WebGL. As with drawing commands, regl consolidates all of these configuration parameters into one function. Here are some examples of how to create a texture,

// From size parameters
var emptyTexture = regl.texture({
  shape: [16, 16]
})

// From a flat array
var typedArrayTexture = regl.texture({
  width: 2,
  height: 2,
  data: [
    255, 255, 255, 255, 0, 0, 0, 0,
    255, 0, 255, 255, 0, 0, 255, 255
  ]
})

// From a square array
var nestedArrayTexture = regl.texture([
  [ [0, 255, 0],  [255, 0, 0] ],
  [ [0, 0, 255], [255, 255, 255] ]
])

// From an ndarray-like object
var ndarrayTexture = regl.texture(require('baboon-image'))

// Manual mipmap specification
var mipmapTexture = regl.texture({
  min: 'mipmap'
})

// From an image element
var image = new Image()
image.src = 'http://mydomain.com/myimage.png'
image.onload = function () {
  var imageTexture = regl.texture(image)
}


// From a canvas
var canvas = document.createElement(canvas)
var context2D = canvas.getContext('2d')
var canvasTexture = regl.texture(canvas)
var otherCanvasTexture = regl.texture(context2D)

// From a video element
var video = document.querySelector('video')
var videoTexture = regl.texture(video)

// From the pixels in the current frame buffer
var copyPixels = regl.texture({
  x: 5,
  y: 1,
  width: 10,
  height: 10,
  copy: true
})

A data source from an image can be one of the following types:

Data typeDescription
Rectangular array of arraysInterpreted as 2D array of arrays
Typed arrayA binary array of pixel values
ArrayInterpreted as array of pixel values with type based on the input type
ndarrayAny object with a shape, stride, offset, data (see SciJS ndarray)
ImageAn HTML image element
VideoAn HTML video element
CanvasA canvas element
Context 2DA canvas 2D context
PropertyDescriptionDefault
widthWidth of texture0
heightHeight of texture0
magSets magnification filter (see table)'nearest'
minSets minification filter (see table)'nearest'
wrapSSets wrap mode on S axis (see table)'clamp'
wrapTSets wrap mode on T axis (see table)'clamp'
anisoSets number of anisotropic samples, requires EXT_texture_filter_anisotropic0
formatTexture format (see table)'rgba'
typeTexture type (see table)'uint8'
dataInput data (see below)
mipmapSee below for a descriptionfalse
flipYFlips textures vertically when uploadingfalse
alignmentSets unpack alignment per row1
premultiplyAlphaPremultiply alpha when unpackingfalse
colorSpaceSets colorspace conversion'none'
dataImage data for the texturenull
channelsNumber of channels for the texture formatnull
  • mipmap. If boolean, then it sets whether or not we should regenerate the mipmaps. If a string, it allows you to specify a hint to the mipmap generator. It can be one of the hints below
Mipmap HintDescription
'don't care', 'dont care'gl.DONT_CARE
'nice'gl.NICEST
'fast'gl.FASTEST

and if a hint is specified, then also the mipmaps will be regenerated. Finally, mipmap can also be an array of arrays. In this case, every subarray will be one of the mipmaps, and you can thus use this option to manually specify the mipmaps of the image. Like this:

regl.texture({
  shape: [4, 4],
  mipmap: [
    [ 0, 1, 2, 3,
      4, 5, 6, 7,
      8, 9, 10, 11,
      12, 13, 14, 15 ],
    [ 0, 1,
      2, 3 ],
    [ 0 ]
  ]
})
  • shape can be used as an array shortcut for [width, height, channels] of image
  • channels can be used to set the number of color channels of the texture. Examples:
var t1 = regl.texture({width: 1, height: 1, channels: 3}) // 'format' will be 'rgb'
var t2 = regl.texture({shape: [2, 2, 2]}) // 'format' will be 'luminance alpha'
var t3 = regl.texture({shape: [2, 2, 4]}) // 'format' will be 'rgba'

So it can be used as an alternative to format.

  • radius can be specified for square images and sets both width and height
  • data can take one of the following values,
  • If an image element is specified and not yet loaded, then regl will upload a temporary image and hook a callback on the image
  • mag sets gl.MAG_FILTER for the texture and can have one of the following values
Mag filterDescription
'nearest'gl.NEAREST
'linear'gl.LINEAR
  • min sets gl.MIN_FILTER for the texture, and can take on one of the following values,
Min filterDescription
'nearest'gl.NEAREST
'linear'gl.LINEAR
'mipmap', 'linear mipmap linear'gl.LINEAR_MIPMAP_LINEAR
'nearest mipmap linear'gl.NEAREST_MIPMAP_LINEAR
'linear mipmap nearest'gl.LINEAR_MIPMAP_NEAREST
'nearest mipmap nearest'gl.NEAREST_MIPMAP_NEAREST
  • wrap can be used as an array shortcut for [wrapS, wrapT]
  • wrapS and wrapT can have any of the following values,
Wrap modeDescription
'repeat'gl.REPEAT
'clamp'gl.CLAMP_TO_EDGE
'mirror'gl.MIRRORED_REPEAT
  • format determines the format of the texture and possibly the type. Possible values for format include,
FormatDescriptionChannelsTypesCompressed?Extension?
'alpha'gl.ALPHA1'uint8','half float','float'โœ–
'luminance'gl.LUMINANCE1'uint8','half float','float'โœ–
'luminance alpha'gl.LUMINANCE_ALPHA2'uint8','half float','float'โœ–
'rgb'gl.RGB3'uint8','half float','float'โœ–
'rgba'gl.RGBA4'uint8','half float','float'โœ–
'rgba4'gl.RGBA44'rgba4'โœ–
'rgb5 a1'gl.RGB5_A14'rgb5 a1'โœ–
'rgb565'gl.RGB5653'rgb565'โœ–
'srgb'ext.SRGB3'uint8','half float','float'โœ–EXT_sRGB
'srgba'ext.RGBA4'uint8','half float','float'โœ–EXT_sRGB
'depth'gl.DEPTH_COMPONENT1'uint16','uint32'โœ–WEBGL_depth_texture
'depth stencil'gl.DEPTH_STENCIL2'depth stencil'โœ–WEBGL_depth_texture
'rgb s3tc dxt1'ext.COMPRESSED_RGB_S3TC_DXT1_EXT3'uint8'โœ“WEBGL_compressed_texture_s3tc
'rgba s3tc dxt1'ext.COMPRESSED_RGBA_S3TC_DXT1_EXT4'uint8'โœ“WEBGL_compressed_texture_s3tc
'rgba s3tc dxt3'ext.COMPRESSED_RGBA_S3TC_DXT3_EXT4'uint8'โœ“WEBGL_compressed_texture_s3tc
'rgba s3tc dxt5'ext.COMPRESSED_RGBA_S3TC_DXT5_EXT4'uint8'โœ“WEBGL_compressed_texture_s3tc
'rgb atc'ext.COMPRESSED_RGB_ATC_WEBGL3'uint8'โœ“WEBGL_compressed_texture_atc
'rgba atc explicit alpha'ext.COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL4'uint8'โœ“WEBGL_compressed_texture_atc
'rgba atc interpolated alpha'ext.COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL4'uint8'โœ“WEBGL_compressed_texture_atc
'rgb pvrtc 4bppv1'ext.COMPRESSED_RGB_PVRTC_4BPPV1_IMG3'uint8'โœ“WEBGL_compressed_texture_pvrtc
'rgb pvrtc 2bppv1'ext.COMPRESSED_RGB_PVRTC_2BPPV1_IMG3'uint8'โœ“WEBGL_compressed_texture_pvrtc
'rgba pvrtc 4bppv1'ext.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG4'uint8'โœ“WEBGL_compressed_texture_pvrtc
'rgba pvrtc 2bppv1'ext.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG4'uint8'โœ“WEBGL_compressed_texture_pvrtc
'rgb etc1'ext.COMPRESSED_RGB_ETC1_WEBGL3'uint8'โœ“WEBGL_compressed_texture_etc1
  • In many cases type can be inferred from the format and other information in the texture. However, in some situations it may still be necessary to set it manually. In such an event, the following values are possible,
TypeDescription
'uint8'gl.UNSIGNED_BYTE
'uint16'gl.UNSIGNED_SHORT
'uint32'gl.UNSIGNED_INT
'float', 'float32'gl.FLOAT
'half float', 'float16'ext.HALF_FLOAT_OES
  • colorSpace sets the WebGL color space flag for pixel unpacking
Color spaceDescription
'none'gl.NONE
'browser'gl.BROWSER_DEFAULT_WEBGL
  • alignment sets the pixel unpack alignment and must be one of [1, 2, 4, 8]

Relevant WebGL APIs

Texture update

Like buffers, textures can be reinitialized in place. Calling the texture as a function re-evaluates the constructor and initializes the texture to a new value:

// First we create a texture
const myTexture = regl.texture()

// Then we can reinitialize it
myTexture({
  width: 10,
  height: 10
})

Doing this lets you defer texture construction or reuse texture objects.

Relevant WebGL APIs

Texture subimage

It is also possible to update a subset of a texture contained in a rectangle. This can be done using the subimage() method of the texture:

const myTexture = regl.texture(4, 4)

myTexture.subimage({
  width: 1,
  height: 1,
  data: [255, 0, 0, 255]
}, 1, 1)

For textures, subimage takes 4 arguments:

texture.subimage(data[, x, y, level])

Where,

  • data is an image data object, similar to the arguments for the texture constructor
  • x, y is the offset of the subimage within the texture (default 0,0)
  • level is the miplevel to run the subimage within (default 0)

Relevant WebGL APIs

Texture resize

Finally, textures can be resized with the .resize() method. Note that this clears the contents of the texture and is not supported by compressed textures.

var texture = regl.texture(5)

texture.resize(3, 7)
Texture properties

The following properties contains information about the texture.

PropertyDescription
widthWidth of texture
heightHeight of texture
formatTexture Format
typeTexture Type
magTexture magnification filter
minTexture minification filter
wrapSTexture wrap mode on S axis
wrapTTexture wrap mode on T axis

They can be accessed after texture creation like this:

var t = regl.texture({
  shape: [16, 16],
  min: 'nearest mipmap linear',
  mag: 'linear',
  wrapS: 'mirror',
  wrapT: 'repeat',
  format: 'rgb',
  type: 'uint8'
})

console.log('tex info: ', t.width, t.height, t.min, t.mag, t.wrapS, t.wrapT, t.format, t.type)

Texture destructor

Finally, when a texture is no longer needed it can be released by calling the destroy() method:

var myTexture = regl.texture({ ... })

myTexture.destroy()

Relevant WebGL APIs

Texture profiling

The following stats are tracked for each texture in the .stats property:

StatisticMeaning
sizeThe size of the texture in bytes

Cube maps

Cube map constructor

Cube maps follow similar syntax to textures. They are created using regl.cube()

// We can allocate a cubemap by giving just the size of the an edge:
const emptyCube = regl.cube(16)

// We can also specify each face individually
const posX = new Image()
const negX = new Image()
// ... etc
const cubeMap = regl.cube(
  posX,
  negX,
  posY,
  negY,
  posZ,
  negZ)

// Or we can initialize each face using an array
const anotherCubeMap = regl.cube({
  radius: 4,
  faces: [
    [
      0, 0, 0, 255, 255, 0, 0, 255,
      0, 255, 0, 255, 0, 0, 255, 255
    ],
    // ...
  ]
})

Cube map update

Cube maps can be reinitialized like textures or buffers:

const cube = regl.cube(8)

// Reset cube map
cube(4)

// Resize cube map
cube.resize(16)
Cube map subimage

Sub-rectangles of faces of cube maps can be updated again using .subimage.

const cube = regl.cube(4)

cube.subimage(0, [
  0, 0, 0, 255, 0, 255, 0, 255,
  255, 0, 0, 255, 0, 0, 255, 255
])

cube.subimage takes the following arguments:

cube.subimage(face, data[, x, y, miplevel])

Relevant WebGL APIs

Cube map resize

Cube maps can be resized in place using the .resize() method. This takes one argument which is the size of the cube map.

var cubemap = regl.cube({ ... })

cubemap.resize(16)

Cube map properties

The following properties contains information about the cube map.

PropertyDescription
widthWidth of a single cube map face
heightHeight of a single cube map face
formatTexture Format
typeTexture Type
magTexture magnification filter
minTexture minification filter
wrapSTexture wrap mode on S axis
wrapTTexture wrap mode on T axis

They can be accessed after cube map creation like this:

var c = regl.cube({
  width: 2,
  height: 2
})

console.log('cube: ', c.width, c.height, c.format, c.type, c.mag, c.min, c.wrapS, c.wrapT)

Cube map profiling

The following stats are tracked for each cube map in the .stats property:

StatisticMeaning
sizeThe size of the cube map in bytes

Cube map destructor

cubeMap.destroy()

Relevant WebGL APIs


Renderbuffers

Renderbuffer constructor

// Allocate a new renderbuffer with the prescribed format
var rb = regl.renderbuffer({
  width: 16,
  height: 16,
  format: 'rgba4'
})

// Allocate an 'rgba4' renderbuffer with a fixed size
var rgba_16x24 = regl.renderbuffer(16, 24)
PropertyInterpretationDefault
'format'Sets the internal format of the render buffer (see below)'rgba4'
'width'Sets the width of the render buffer in pixels1
'height'Sets the height of the render buffer in pixels1
'shape'Alias for width and height[1,1]
'radius'Simultaneously sets width and height1
FormatDescription
'rgba4'gl.RGBA4
'rgb565'gl.RGB565
'rgb5 a1'gl.RGB5_A1
'depth'gl.DEPTH_COMPONENT16
'stencil'gl.STENCIL_INDEX8
'depth stencil'gl.DEPTH_STENCIL
'srgba'ext.SRGB8_ALPHA8_EXT, only if EXT_sRGB supported
'rgba16f'16 bit floating point RGBA buffer, only if EXT_color_buffer_half_float
'rgb16f'16 bit floating point RGB buffer, only if EXT_color_buffer_half_float
'rgba32f'32 bit floating point RGBA buffer, only if WEBGL_color_buffer_float supported

Relevant WebGL APIs

Renderbuffer update

Like all other resources, renderbuffers can be updated in place:

var renderbuffer = regl.renderbuffer()

renderbuffer({
  radius: 3,
  format: 'depth'
})
Renderbuffer resize

A renderbuffer can also be resized in place by calling .resize():

var renderbuffer = regl.renderbuffer({
  shape: [10, 10],
  format: 'depth stencil'
})

renderbuffer.resize(32, 32)

Renderbuffer properties

The following properties contains information about the renderbuffer.

PropertyDescription
widthWidth of the renderbuffer
heightHeight of the renderbuffer
formatFormat of the renderbuffer

They can be accessed after renderbuffer creation like this:

var r = regl.renderbuffer({shape: [1, 1],
  format: 'rgb5 a1'
})

console.log('renderbuffer: ', r.width, r.height, r.format)

Renderbuffers destructor

rb.destroy()

Relevant WebGL APIs

Renderbuffer profiling

The following stats are tracked for each renderbuffer in the .stats property:

StatisticMeaning
sizeThe size of the renderbuffer in bytes

Framebuffers

Framebuffer constructor

// Creating a simple 2x2 framebuffer:
var fbo2x2 = regl.framebuffer(2)

// A 256x256 framebuffer without a stencil attachment
var fbo = regl.framebuffer({
  width: 256,
  height: 256,
  stencil: false
})

// A framebuffer with a color buffer
var texture = regl.texture(16)
var texFBO = regl.framebuffer({
  color: texture
})
PropertyDescriptionDefault
widthSets the width of the framebuffergl.drawingBufferWidth
heightSets the height of the framebuffergl.drawingBufferHeight
colorAn optional array of either textures renderbuffers for the color attachment.
depthIf boolean, then toggles the depth attachment. Otherwise if a renderbuffer/texture sets the depth attachment.true
stencilIf boolean, then toggles the stencil attachment. Otherwise if a renderbuffer sets the stencil attachment.true
depthStencilIf boolean, then toggles both the depth and stencil attachment. Otherwise if a renderbuffer/texture sets the combined depth/stencil attachment.true
colorFormatSets the format of the color buffer. Ignored if color'rgba'
colorTypeSets the type of the color buffer if it is a texture'uint8'
colorCountSets the number of color buffers. Values > 1 require WEBGL_draw_buffers1
depthTextureToggles whether depth/stencil attachments should be in texture. Requires WEBGL_depth_texturefalse
Color formatDescriptionAttachmentNotes
'rgba'gl.RGBATexture
'rgba4'gl.RGBA4Renderbuffer
'rgb565'gl.RGB565Renderbuffer
'rgb5 a1'gl.RGB5_A1Renderbuffer
'rgb16f'gl.RGB16FRenderbufferonly if EXT_color_buffer_half_float
'rgba16f'gl.RGBA16FRenderbufferonly if EXT_color_buffer_half_float
'rgba32f'gl.RGBA32FRenderbufferonly if WEBGL_color_buffer_float supported
'srgba'gl.SRGB8_ALPHA8Renderbufferonly if EXT_sRGB supported
Color typeDescription
'uint8'gl.UNSIGNED_BYTE
'half float'16 bit float, requires OES_texture_half_float
'float'32 bit float, requires OES_texture_float

Notes

  • If color is not specified, then color attachments are created automatically
  • Instead of passing width/height, it is also possible to pass in shape to the framebuffer constructor.

Relevant WebGL APIs

Framebuffer update

Like all other objects, a framebuffer can be updated in place:

var framebuffer = regl.framebuffer(3, 4)

framebuffer({
  shape: [8, 10],
  depth: false
})
Framebuffer binding

For convenience it is possible to bind a framebuffer directly. This is a short cut for creating a command which sets the framebuffer:

var framebuffer = regl.framebuffer(5)

framebuffer.use(function () {
  // now we can draw to the framebuffer
})

//
// This is the same as doing the following:
//
var setFBO = regl({
  framebuffer: framebuffer
})

setFBO(function () {
  // .. same situation as above
})
Framebuffer resize

Framebuffers can be resized using the .resize() method. This method will also modify all of the framebuffer's attachments.

var framebuffer = regl.framebuffer(20, 4)

framebuffer.resize(3, 3)

// set both width and height to 3.
framebuffer.resize(3)

Framebuffer destructor

Calling .destroy() on a framebuffer removes it and recursively destroys any non-shared attachments.

fbo.destroy()

Relevant WebGL APIs


Cubic frame buffers

Cube framebuffer constructor

var cubeFbo = regl.framebufferCube(512)

var cubeAlt = regl.framebufferCube({
  radius: 32,
  color: regl.cube(32),
  depth: false,
  stencil: false
})
ParameterDescription
radiusThe size of the cube buffer
colorThe color buffer attachment
colorFormatFormat of color buffer to create
colorTypeType of color buffer
colorCountNumber of color attachments
depthDepth buffer attachment
stencilStencil buffer attachment
depthStencilDepth-stencil attachment
Color formatDescriptionAttachment
'rgba'gl.RGBATexture
Color typeDescription
'uint8'gl.UNSIGNED_BYTE
'half float'16 bit float, requires OES_texture_half_float
'float'32 bit float, requires OES_texture_float

Notes

  • The specified depth/stencil/depth-stencil attachment will be reused for all 6 cube faces.

Cube framebuffer update

// reinitialize
fboCube({
  radius: 10
})
Cube framebuffer resize
fboCube.resize(16)

Cube framebuffer destructor

fboCube.destroy()

Other tasks

Other than draw commands and resources, there are a few miscellaneous parts of the WebGL API which REGL wraps for completeness.


Clear the draw buffer

regl.clear combines gl.clearColor, gl.clearDepth, gl.clearStencil and gl.clear into a single procedure, which has the following usage:

regl.clear({
  color: [0, 0, 0, 1],
  depth: 1,
  stencil: 0
})
PropertyDescription
colorSets the clear color
depthSets the clear depth value
stencilSets the clear stencil value
framebufferSets the target framebuffer to clear (if unspecified, uses the current framebuffer object)

If an option is not present, then the corresponding buffer is not cleared

Relevant WebGL APIs


Reading pixels

// read entire screen
var snapshot = regl.read()

// Can also reuse a buffer by passing it to regl.read()
var bytes = new Uint8Array(100)
regl.read(bytes)

// It is also possible to specify a region to read from
var pixels = regl.read({
  x: 2,
  y: 3,
  width: 3,
  height: 1,
  data: new Uint8Array(12)
})

// You can also read from the currently bound fbo.
// Note that `pixels` will be of type `Float32Array`
// in this case.
fbo = regl.framebuffer({
  width: W,
  height: H,
  colorFormat: 'rgba',
  colorType: 'float'
})
regl({framebuffer: fbo})(() => {
  regl.clear({color: [0.5, 0.25, 0.5, 0.25]})
  var pixels = regl.read()
})
PropertyDescriptionDefault
dataAn optional ArrayBufferView which gets the result of reading the pixelsnull
xThe x-offset of the lower-left corner of the rectangle in pixels0
yThe y-offset of the lower-left corner of the rectangle in pixels0
widthThe width of the rectangle in pixelsCurrent framebuffer width
heightThe height of the rectangle in pixelsCurrent framebuffer height
framebufferSets the framebuffer to read pixels fromThe currently bound framebuffer

Notes

  • In order to read pixels from the drawing buffer, you must create your webgl context with preserveDrawingBuffer set to true. If this is not set, then regl.read will throw an exception.

  • You can only read pixels from a framebuffer of type 'uint8' or 'float'. Furthermore, it is not possible to read from a renderbuffer.

Relevant WebGL APIs


Per-frame callbacks

regl also provides a common wrapper over requestAnimationFrame and cancelAnimationFrame that integrates gracefully with context loss events. regl.frame() also calls gl.flush and drains several internal buffers, so you should try to do all your rendering to the drawing buffer within the frame callback.

// Hook a callback to run each frame
var tick = regl.frame(function (context) {

  // context is the default state of the regl context variables

  // ...
})

// When we are done, we can unsubscribe by calling cancel on the callback
tick.cancel()

It is possible to manage framecallbacks manually, however before any loop it is essential to call regl.poll() which updates all timers and viewports.


Extensions

In regl, extensions must be declared before they can be used. An extension may be specified as a 'hard' requirement, meaning that if it is not present then context creation fails or as a 'soft' requirement. This can be done by passing a list of extensions to the extensions and optionalExtensions fields in the regl constructor respectively.

require('regl')({
  extensions: ['OES_texture_float'],
  optionalExtensions: ['oes_texture_float_linear'],
  onDone: function (err, regl) {
    if (err) {
      console.log(err)
      return
    }

    // now we can use regl as usual

    if (regl.hasExtension('oes_texture_float_linear')) {
      // can use texture float linear
    }
  }
})

The method regl.hasExtension() can be used to test if an extension is present. Arguments to regl.hasExtension() are case insensitive.

For more information on WebGL extensions, see the WebGL extension registry.

Relevant WebGL APIs


Device capabilities and limits

regl exposes info about the WebGL context limits and capabilities via the regl.limits object. The following properties are supported,

PropertyDescription
colorBitsAn array of bits depths for the red, green, blue and alpha channels
depthBitsBit depth of drawing buffer
stencilBitsBit depth of stencil buffer
subpixelBitsgl.SUBPIXEL_BITS
extensionsA list of all supported extensions
maxAnisotropicMaximum number of anisotropic filtering samples
maxDrawbuffersMaximum number of draw buffers
maxColorAttachmentsMaximum number of color attachments
pointSizeDimsgl.ALIASED_POINT_SIZE_RANGE
lineWidthDimsgl.ALIASED_LINE_WIDTH_RANGE
maxViewportDimsgl.MAX_VIEWPORT_DIMS
maxCombinedTextureUnitsgl.MAX_COMBINED_TEXTURE_IMAGE_UNITS
maxCubeMapSizegl.MAX_CUBE_MAP_TEXTURE_SIZE
maxRenderbufferSizegl.MAX_RENDERBUFFER_SIZE
maxTextureUnitsgl.MAX_TEXTURE_IMAGE_UNITS
maxTextureSizegl.MAX_TEXTURE_SIZE
maxAttributesgl.MAX_VERTEX_ATTRIBS
maxVertexUniformsgl.MAX_VERTEX_UNIFORM_VECTORS
maxVertexTextureUnitsgl.MAX_VERTEX_TEXTURE_IMAGE_UNITS
maxVaryingVectorsgl.MAX_VARYING_VECTORS
maxFragmentUniformsgl.MAX_FRAGMENT_UNIFORM_VECTORS
glslgl.SHADING_LANGUAGE_VERSION
renderergl.RENDERER
vendorgl.VENDOR
versiongl.VERSION

Relevant WebGL APIs


Performance metrics

regl tracks several metrics for performance monitoring. These can be read using the regl.stats object:

MetricMeaning
bufferCountThe number of array buffers currently allocated
elementsCountThe number of element buffers currently allocated
framebufferCountThe number of framebuffers currently allocated
shaderCountThe number of shaders currently allocated
textureCountThe number of textures currently allocated
cubeCountThe number of cube maps currently allocated
renderbufferCountThe number of renderbuffers currently allocated
getTotalTextureSize()The total amount of memory allocated for textures and cube maps
getTotalBufferSize()The total amount of memory allocated for array buffers and element buffers
getTotalRenderbufferSize()The total amount of memory allocated for renderbuffers
getMaxUniformsCount()The maximum number of uniforms in any shader
getMaxAttributesCount()The maximum number of attributes in any shader
maxTextureUnits()The maximum number of texture units used

Clocks and timers

It may be desirable to synchronize external events (like button presses or mouse movements) with the internal timer in regl. To sample the current time stamp outside of the frame callback you can use the following command:

// Samples current timestamp of regl's local clock
regl.now()

Clean up

When a regl context is no longer needed, it can be destroyed releasing all associated resources with the following command:

regl.destroy()

Context loss

regl makes a best faith effort to handle context loss by default. This means that buffers and textures are reinitialized on a context restore with their contents. This can be done using the context loss events exposed by regl. For example:

var regl = require('regl')()

regl.on('lost', function () {
  console.log('lost webgl context')
})

regl.on('restore', function () {
  console.log('webgl context restored')
})

Unsafe escape hatch

WARNING: regl is designed in such a way that you should never have to directly access the underlying WebGL context. However, if you really absolutely need to do this for some reason (for example to interface with an external library), you can still get a reference to the WebGL context. Note though that if you do this you will need to restore the regl state in order to prevent rendering errors. This can be done with the following unsafe methods:

// This retrieves a reference to the underlying WebGL context (don't do this!)
var gl = regl._gl

//  ... do some crazy direct manipulation here

// now restore the regl state
regl._refresh()

// Resume using regl as normal

Note that you must call regl._refresh() if you have changed the WebGL state.


Tips

The following are some random tips for writing WebGL programs. Some are regl specific and some are more generic.

Reuse commands

Creating commands in regl is expensive because regl does many complex optimizations up front in order to ensure the best possible performance. As a result, it is expected that users should declare commands once and then call them many times. For example:

// Good usage:
var command = regl({
  vert: `...`,
  frag: `...`
})

regl.frame(() => {
  command()
})

Do not generate a command in your frame loop:

// BAD! Do not do this!
regl.frame(() => {
  // This creates a new command object and executes it each frame.
  // It will be very slow.
  regl({
    vert: `...`,
    frag: `...`
  })()
})

Reuse resources (buffers, elements, textures, etc.)

Similarly, you should reuse buffers and textures wherever possible. If you are continually uploading data to the GPU you should reuse whatever buffers or textures you can. For example, suppose you want to play a video. Then it is better to reuse the buffer as follows:

// Get a reference to the video element
const myVideo = document.querySelector('video')

// Create a video texture
const videoTexture = regl.texture(myVideo)

regl.frame(() => {
  // Update the frames of the video
  videoTexture.subimage(myVideo)
})

For dynamic buffers or elements, remember to allocate them using stream or dynamic usage.

Preallocate memory

The most common cause of jank in JavaScript applications is garbage collection. In general, the only way to avoid this is to not allocate temporary objects. To avoid this in regl you can reuse parameter objects which are passed to commands and preallocate arrays/matrices.

Removing assertions

By default, regl is compiled with a number of assertions, checks and validations to make it easier to find and fix errors. However these assertions will increase your code size and in some cases may slightly slow things down. Fortunately, they can be removed with the help of a transform in bin/remove-check.

Profiling tips

If your application is running too slow and you want to understand what is going on, regl provides many hooks which you can use to monitor and debug your performance.

Context loss mitigation

A WebGL application must be prepared to lose context at any time. This is an unfortunate part of life when working on the web. If this happens regl will make a best faith effort to recover functionality after the context is restored, however it is still up to the user to handle this situation.

Use batch mode

If you want to draw a bunch of copies of the same object, only with different properties, be sure to use batch mode. Commands rendered in batch mode can be optimized by avoiding certain state checks which are required for serial commands.