Skip to main content

Porting Guide

Given that the changes in the v9 API are quite extensive, this separate porting guide is provided to hopefully help plan the upgrade process.

v9 API Design background

We'll start with motivating the changes in v9.

Why is the v9 API a major breaking change?

The v8 API design was focused on providing a set of classes optimized for working with the WebGL2 API, and a significant part of the code was untyped, predating TypeScript introduction.

The v9 API design makes a number breaking changes in order to take full advantage of WebGPU and TypeScript.

Goal: A "WebGPU-first" API

Adding WebGPU support was the top priority for v9. Some consideration was given towards keeping the existing WebGL2-centric API and providing a WebGPU implementation for that API, to avoid breaking applications. But ultimately that did not make sense. The WebGPU API is quite different from WebGL. WebGPU was designed to avoid the performance overhead that is unavoidable in WebGL APIs (e.g. avoid repeated validation of GPU objects, avoid repeated issuing of same commands, avoid constructs that prevent deep shader optimizations etc). WebGPU is clearly the future of GPU APIs in the browser (WebGL is no longer evolving). It seems doubtful that would be able to remain relevant if it stuck to a WebGL-focused API.

Goal: A "TypeScript-first" API

As continued to adopt TypeScript it become clear that modern TypeScript enables a number of simpler, more intuitive API constructions. One example is that in TypeScript we can now safely specify that an "enum type" argument must be one of a few specific strings. By using string values in types, we don't need to introduce enumerations or key-value object constants. As an example the Sampler minFilter property is now specified as either 'linear' or 'nearest', rather than GL.LINEAR or GL.NEAREST. This move to string constant values enabled to align string constants with the WebGPU standard, avoiding the need to define additional mappings and abstractions on top of the WebGPU API.

Goal: Improved Maintainability

Another reason for the breaking changes in v9 is simply the removal and restructuring of legacy code. Over 8 releases, the API had grown quite large. It exposed all the functionality offered by WebGL2, however many WebGL 2 functions were rarely used. contained a lot of code for mutating WebGL resources, which would not work for an applications that also attempts to run on WebGPU, since WebGPU classes are (mostly) immutable. In addition, some core maintainers of have moved on, so we took the opportunity to cut out some legacy code to make sure the code base remains accessible and easy to understand for the community. The new WebGPU compatible API provided the just the lens we needed to decide which particular pieces of functionality should be cut.

Effort Estimation

Suddenly having to upgrade an existing application to a new breaking API version is never fun. A first step for a developer is often to assess how much effort an upgrade is likely to require.

While it is impossible for us to assess here how much work will be required for your specific application, and the list of v9 changes listed below is long, it probably looks worse than it is:

  • While the entire API has been modernized, the overall structure and concepts of the classic API have been preserved.
  • The API abstraction level has not changed, and while constants and names etc will need to be updated, programmers should find themselves comfortably working with the same classes in v9 as they did in v8, without having to re-learn the API.
  • They key building block classes like AnimationLoop, Model, Buffer, Texture etc are still available.
  • Many changes affect parts of the API that are not frequently used in most applications.
  • In addition, the strong TypeScript typings in v9 should create a strong safety net when porting. This means that the typescript compiler should be able to pinpoint most breakages in your application before you even run your code.

Quick v9 API overview

The v9 API is designed to provide portable WebGPU / WebGL 2 API, ground-up TypeScript support, and strong support for working with both WGSL and GLSL shaders.

The new v9 GPU API is now an abstract Device API, meaning that the API itself is specified in terms of TypeScript interfaces and abstract classes that cannot be directly instantiates. Types such as Buffer, Texture etc can not be used on their own, but a subclass that implements the functionality of that class a specific GPU API must be requested from a Device (WebGLBuffer, WebGPUTexture).

The new Device class provides the access point to the new GPU interface. It creates and instruments the WebGL context or the WebGPU adapter, and provides methods to create all the implementation classes (WebGLDevice.createBuffer(... => WebGLBuffer, WebGPUDevice.createTexture(... => WebGLGPUTexture)

The new CanvasContext class handles context sizing, resolution etc.

The v9 API no longer accepts/returns GL constants, but instead uses the corresponding string values from the WebGPU standard (mapping those transparently under WebGL).

  • The parameter API has been updated to more closely match the WebGPU API. Also parameters are specified on pipelines and render pass creation and are and not as easy to change per draw call as they were in v8.

In progress

  • Reading and writing buffers is now an async operation. While WebGL does not support async reads and writes on MacOS, the API is still async to ensure portability to WebGPU.

  • Uniform buffers are now the standard way for the application to specify uniforms. Uniform buffers are "emulated" under WebGL.

A subset of the new interfaces in the v9 API:

InterfaceDescription exposes GPU capabilities on the device in the form of one or more as Adapters.
DeviceManages resources, and the device’s GPUQueues, which execute commands.
BufferThe physical resources backed by GPU memory. A Device may have its own memory with high-speed access to the processing units.
TextureLike buffer, but supports random access
GPUCommandBuffer and GPURenderBundle are containers for user-recorded commands.
ShaderCompiled shader code.
Sampleror GPUBindGroup, configure the way physical resources are used by the GPU.

GPUs execute commands encoded in GPUCommandBuffers by feeding data through a pipeline, which is a mix of fixed-function and programmable stages. Programmable stages execute shaders, which are special programs designed to run on GPU hardware. Most of the state of a pipeline is defined by a GPURenderPipeline or a GPUComputePipeline object. The state not included in these pipeline objects is set during encoding with commands, such as beginRenderPass() or setBlendColor().

Porting Strategy

This is based on the porting strategy used to port, which is the biggest code base.

Step 1: Update Imports

  • Change imports from to
  • Change imports from to

Step 2: Replace WebGLRenderingContext with Device

The quickest way to start porting would be to start updating your application to consistently use the new Device class instead of directly working with .

  • Feature Detection - At the end of this stage it is also possible to replace feature detection constants with the new device.features functionality.

Step 3: Replace canvas manipulation with CanvasContext

The handling of canvas related functionality (size, resolution etc) can be a messy part of WebGL applications.

Step 3: Replace parameter names

Recommended changes:

  • Gradually reduce the number of imports and start adopting string constants.

Upgrading GPU Parameters

  • Parameters are set on Pipeline creation. They can not be modified, or passed as parameters to draw calls.
  • Parameters can now only be set, not queried. no longer provides a way to query parameters.

Depth testing

To set up depth testing

const value = model.setParameters({
depthWriteEnabled: true,
depthCompare: 'less-equal'

Upgrading to v9.0 v9 is a major modernization of the API, so the upgrade notes for this release are unusually long. This page primarily contains a reference list of API changes. To facilitate porting to the v9 release we have also provided a Porting Guide that provides more background information and recommended porting strategies.

Removed Functionality

Going forward, will focus on leveraging the capabilities WebGPU, while maintaining a high-quality, compatible WebGL2 backend. To accelerate this roadmap, v9 is dropping WebGL 1 support.

  • WebGL1 is no longer supported. The backend now always creates a WebGL2 context.
  • GLSL 1.00 is no longer supported. GLSL shaders need to be ported to GLSL 3.00.
  • headless-gl integration for Node.js is no longer supported (as it only supported WebGL 1).

Non-API changes v9 upgrades tooling and packaging to latest JavaScript ecosystem standards:

  • ES modules - is now published as ES modules. This is the modern way of packaging JavaScript applications that fully embraces the static import/export syntax. While most bundlers and Node.js environments have added support for ES modules, they can introduce incompatibilities with code that uses legacy imports. Getting your build tooling and code base ready for ES modules may be a good first step before you start upgrading
  • Dependencies - v9 introduces major version bumps of its key dependencies: v4, v4 and v4. These upgrades ensures that all dependencies also use the same ES module packaging and follow latest typescript standards. Since the new libraries are more strongly typed, some new warnings or errors may result from this upgrade, however we expect that developers will welcome the increased type safety.

Module-level changes

Modulev8 Descriptionv9 Replacement core module was re-exporting other modules.Replace direct use of WebGL classes with device API. import directly from WebGL classes meant for direct usage.Now exports the WebGL backend for the module. now offers the legacy v8 WebGL API. numeric OpenGL constants.Do not use. The v9 API uses strictly typed WebGPU-style strings instead of numeric constants. WebGL context functionality.Removed. WebGL context is now handled by the WebGLDevice and CanvasContext classes.

Core Modules

  • The core module is no longer an umbrella module that simply re-exports exports from other modules.
  • It now provides the abstract Device API (for which and provide backends).

  • The engine module is largely unchanged in that it provides the same classes as before.

  • While the webgl module still contains the WebGL 2 classes, these classes can no longer be imported directly. Instead the application should use Device create methods (device.createBuffer(), device.createTexture() etc to create these ojects in a portable way). (INTERNAL)

  • The constant module remains but is now considered an internal module, and is no longer intended to be imported by applications.
  • In the external API, all WebGL-style numeric constants (GL. constants) have been replaced with strictly typed WebGPU style string constants. (REMOVED)

The debug module has been removed. Debug functionality is now built-in (and dynamically loaded when needed) so there is no longer a need to import a separate debug module.

  • Scene graph exports (ModelNode, GroupNode, ScenegraphNode) has been moved into
  • glTF exports have been moved to (removed, no longer needed, see Upgrade Guide)

Detailed Upgrade Guide


v8 Prop or Methodv9 ReplacementComment
props.glprops.deviceNow accepts a Promise<Device>.
props.createFramebufferN/AYou will need to create framebuffers explicitly.
props.debugluma.createDevice({debug: true})Device debug functionality is improved.
animationLoop.isContextLost()device.lost, device.isLost() /

The core module was re-exporting the webgl classes.

A long list of changes, some required to make the API portable between WebGPU and WebGL, and many to accommodate the limitations of the more locked-down WebGPU API.

new Buffer(gl, props)device.createBuffer(props)WebGL classes such as Buffer, Texture2D etc can no longer be imported and instantiated with . Instead they must be created via a device methods.
ProgramRenderPipelineWebGPU alignment (removed)

The WebGL context functions from etc), have been replaced by methods on to the new Device and CanvasContext classes.

The v8 API was designed to allow apps to work directly with the WebGLRenderingContext object. v9 enables applications to work portably with both WebGPU and WebGL, and accordingly it wraps the WebGL context in a Device instance.

v8 Prop or Methodv9 ReplacementComment
WebGLRenderingContextDevice.glContexts are created (or wrapped) by the Device class.
WebGL2RenderingContextDevice.glContexts are created (or wrapped) by the Device class.
assertWebGLContext(gl)N/AA device will always hold a valid WebGL context
assertWebGL2Context(gl)N/ANot needed. WebGL devices now always use WebGL2.
createGLContext()luma.createDevice()Will create a WebGL context if WebGLDevice is registered.
createGLContext({onContextLost})device.lostPromise that lets the application await context loss.
instrumentGLContext()WebGLDevice.attach(gl)Contexts are now automatically instrumented.
hasFeature(feature)device.features.has(feature)Note: Feature names now defined by WebGPU style strings.
getGLContextInfo(gl)device.infoReturned object is keyed with strings instead of GL constants.
getContextLimits(gl)device.limitsReturns "WebGPU style" limits instead of WebGL style enums
resizeGLContext(gl, options)canvasContext.resize(options)Same options: {width, height, useDevicePixels}
getDevicePixelRatio()canvasContext.getDevicePixelRatio()Uses useDevicePixels prop on the CanvasContext
hasFeature(gl, ...)device.features.has(...)See feature constant mapping below

v8 Prop or Methodv9 ReplacementComment
import GL from {GL} from to use a named export ( => ) for ES module compatibility.**

v8 Prop or Methodv9 ReplacementComment
makeDebugContext()luma.createDevice({debug: true, type: 'webgl'})Khronos WebGL developer tools are dynamically loaded when needed.
Spector.jsluma.createDevice({spector: true, type: 'webgl'})Spector.js is pre-integrated. Te Spector.js library will be dynamically loaded when needed and the canvas is "captured".

Constant Upgrade Guide


Feature constants have been changed to match the WebGPU API and will need to be updated. The strong typing in the v9 API means that TypeScript will detect unsupported feature names which should make the conversion process less painful. v8 FEATUREv9 DeviceFeatureComments
General WebGL Features
FEATURES.WEBGL2webglTrue for WebGL Contexts
FEATURES.TIMER_QUERYtimer-query-webgl[Query][/]ocs/api-reference/webgl/query) for asynchronous GPU timings
FEATURES.INSTANCED_RENDERINGN/A (always true)Instanced rendering (via instanced vertex attributes)
FEATURES.VERTEX_ARRAY_OBJECTN/A (always true)VertexArrayObjects can be created
FEATURES.ELEMENT_INDEX_UINT32N/A (always true)32 bit indices available for GL.ELEMENT_ARRAY_BUFFERs
FEATURES.BLEND_MINMAXN/A (always true)GL.MIN, GL.MAX blending modes are available
FEATURES.FRAGMENT_SHADER_DRAW_BUFFERSN/A (always true)Fragment shader can draw to multiple render targets
FEATURES.FRAGMENT_SHADER_DEPTHN/A (always true)Fragment shader can control fragment depth value
FEATURES.SHADER_TEXTURE_LODN/A (always true)Enables shader control of LOD
FEATURES.FRAGMENT_SHADER_DERIVATIVESN/A (always true)Derivative functions are available in GLSL
Textures and Framebuffers
FEATURES.TEXTURE_FLOATtexture-renerable-float32-webglFloating point textures can be created / set as samplers
FEATURES.TEXTURE_HALF_FLOATfloat16-renderable-webglHalf float textures can be created and set as samplers
FEATURES.MULTIPLE_RENDER_TARGETSmultiple-renderable--webgl1Framebuffer multiple color attachments that fragment shaders can access
FEATURES.COLOR_ATTACHMENT_RGBA32Frgba32float-renderable-webgl1Floating point Texture in the GL.RGBA32F format are renderable & readable
FEATURES.COLOR_ATTACHMENT_FLOATwebgl1Floating point Textures renderable + readable.
FEATURES.COLOR_ATTACHMENT_HALF_FLOATwebgl1Half float format Textures are renderable and readable
FEATURES.FLOAT_BLENDtexture-blend-float-webglBlending with 32-bit floating point color buffers
TEXTURE_FILTER_LINEAR_FLOATfloat32-filterable-linear-webgl1Linear texture filtering for floating point textures
FEATURES.TEXTURE_FILTER_LINEAR_HALF_FLOATfloat16-filterable-linear-webgl1Linear texture filtering for half float textures
FEATURES.TEXTURE_FILTER_ANISOTROPICtexture-filterable-anisotropic-webglAnisotropic texture filtering
FEATURES.SRGBtexture-formats-srgb-webgl1sRGB encoded rendering is available
FEATURES.TEXTURE_DEPTH_BUFFERStexture-formats-depth-webgl1Depth buffers can be stored in Textures, e.g. for shadow map calculations

GPU Parameters

| setParameters | | Parameters can no longer be set on the WebGL context. They must be set on a RenderPipeline. | | setParameters || - RenderPipeline parameters cannot be changed after creation (though the Model class will create new RenderPipeline instances if parameters change).

  • Some parameters are set on RenderPass.
  • Constant attributes are no longer supported.
  • clear can no longer be called directly. Instead attachments are cleared when a RenderPass is created and clear colors must be specified in beginRenderPass.
  • Uniform buffers are required to run under WebGPU.

Parameter Mapping

The following table shows mappings from luma v8 WebGL parameters to luma v9 WebGPU style parameters.

luma v8 / WebGL Parameterv9 ParameterValuesv9 Values
polygonOffsetdepthBias, depthBiasSlopeScale
Rasterization Parameters
cullFacecullModeWhich face to cull'none', 'front', 'back'
frontFacefrontFaceWhich triangle winding order is frontccw, cw
polygonOffsetdepthBiasSmall depth offset for polygonsfloat
polygonOffsetdepthBiasSlopeScaleSmall depth factor for polygonsfloat
polygonOffsetdepthBiasClampMax depth offset for polygonsfloat
Stencil Parameters
stencilMask / GL.STENCIL_WRITEMASKstencilReadMaskBinary mask for reading stencil valuesnumber (0xffffffff)
stencilFunc / GL.STENCIL_VALUE_MASKstencilWriteMaskBinary mask for writing stencil valuesnumber (0xffffffff)
stencilFunc / GL.STENCIL_FUNCstencilCompareHow the mask is comparedalways, not-equal, ...
stencilOp / GL.STENCIL_PASS_DEPTH_PASSstencilPassOperation'keep'
stencilOp / GL.STENCIL_PASS_DEPTH_FAILstencilDepthFailOperation'keep'
stencilOp / GL.STENCIL_FAILstencilFailOperation'keep'


TODO - this section needs updating

WebGL FunctionWebGL v9 counterpart
stencilFunc[GL.STENCIL_FUNC, GL.STENCIL_REF, GL.STENCIL_VALUE_MASK]stencilCompare, stencilReadMask
stencilOpGL.STENCIL_FAIL, `,stencilPassOperation, `stencilFailDepth
GL.STENCIL_TESTfalseEnables stencil testing
GL.STENCIL_CLEAR_VALUE0Sets index used when stencil buffer is cleared.
GL.STENCIL_WRITEMASK0xFFFFFFFFSets bit mask enabling writing of individual bits in the stencil planes
GL.STENCIL_BACK_WRITEMASK0xFFFFFFFFSets bit mask enabling writing of individual bits in the stencil planes
GL.STENCIL_BACK_VALUE_MASK0xFFFFFFFFSets bit mask enabling writing of individual bits in the stencil planes
GL.STENCIL_FAILGL.KEEPstencil test fail action
GL.STENCIL_BACK_FAILGL.KEEPstencil test fail action, back
GL.STENCIL_BACK_PASS_DEPTH_FAILGL.KEEPdepth test fail action, back
GL.STENCIL_BACK_PASS_DEPTH_PASSGL.KEEPdepth test pass action, back
GL.BLEND_COLORFloat32Array(4)[0, 0, 0, 0]
  • Depth Bias - Sometimes referred to as "polygon offset". Adds small offset to fragment depth values (by factor × DZ + r × units). Usually used as a heuristic to avoid z-fighting, but can also be used for effects like applying decals to surfaces, and for rendering solids with highlighted edges. The semantics of polygon offsets are loosely specified by the WebGL standard and results can thus be driver dependent.

After the fragment shader runs, optional stencil tests are performed, with resulting operations on the the stencil buffer.

V8/WebGL FunctionDescriptionValues
Stencil Parameters
stencilReadMaskBinary mask for reading stencil valuesnumber (0xffffffff)
stencilWriteMaskBinary mask for writing stencil valuesnumber (0xffffffff)gl.frontFace
stencilCompareHow the mask is comparedalways, not-equal,

Action when the stencil test fails

  • stencil test fail action,
  • depth test fail action,
  • pass action


  • By using binary masks, an 8 bit stencil buffer can effectively contain 8 separate masks or stencils
  • The API currently does not support setting stencil operations separately for front and back faces.
WebGL FunctionWebGL v9 counterpart
stencilFunc[GL.STENCIL_FUNC, GL.STENCIL_REF, GL.STENCIL_VALUE_MASK]stencilCompare, stencilReadMask


  • In WebGL, setting any value will enable stencil testing (i.e. enable GL.STENCIL_TEST).

  • In WebGL, setting any value will enable stencil testing (i.e. enable GL.STENCIL_TEST).

Clear Color

FunctionSets parameters
GL.COLOR_CLEAR_VALUEnew Float32Array(4)[0, 0, 0, 0].

Color Mask

FunctionSets parameters
GL.COLOR_WRITEMASK[GLboolean, GLboolean, GLboolean, GLboolean][true, true, true, true].


GL.DITHERGLbooleantrueEnable dithering of color components before they get written to the color buffer
  • Note: Dithering is driver dependent and typically has a stronger effect when the color components have a lower number of bits.


Add small offset to fragment depth values (by factor × DZ + r × units) Useful for rendering hidden-line images, for applying decals to surfaces, and for rendering solids with highlighted edges.

FunctionSets parameters
  • Note: The semantics of polygon offsets are loosely specified by the WebGL standard and results can thus be driver dependent.

Rasterization (WebGL 2)

Primitives are discarded immediately before the rasterization stage, but after the optional transform feedback stage. gl.clear() commands are ignored.

GL.RASTERIZER_DISCARDGLbooleanfalseDisable rasterization


Specify multisample coverage parameters

FunctionSets parameters
GL_SAMPLE_COVERAGEGLbooleanfalseActivates the computation of a temporary coverage value determined by the alpha value.
GL_SAMPLE_ALPHA_TO_COVERAGEGLbooleanfalseActivates ANDing the fragment's coverage with the temporary coverage value

Scissor Test

Settings for scissor test and scissor box.

FunctionSets parameters
GL.SCISSOR_BOXInt32Array(4)[null, null, null, null]), // TBD


Specifies the transformation from normalized device coordinates to window/framebuffer coordinates. The maximum supported value, is defined by the GL.MAX_VIEWPORT_DIMS limit.

GL.VIEWPORTInt32Array(4)[...] TBDViewport


// Set viewport to maximum supported size
const maxViewport = getLimits(gl)[GL.MAX_VIEWPORT_DIMS];
setState(gl, {
viewport: [0, 0, maxViewport[0], maxViewport[1]]


GPU State Management can be quite complicated.

  • A large part of the WebGL API is devoted to parameters. When reading, querying individual values using GL constants is the norm, and when writing, special purpose functions are provided for most parameters. supports both forms for both reading and writing parameters.
  • Reading values from WebGL can be very slow if it requires a GPU roundtrip. To get around this, reads values once, caches them and tracks them as they are changed through luma functions. The cached values can get out of sync if the context is shared outside of
  •'s state management enables "conflict-free" programming, so that even when setting global state, one part of the code does not need to worry about whether other parts are changing the global state.
  • Note that to fully support the conflict-free model and detect changes done e.g. in other WebGL libraries, needs to hook into the WebGL context to track state changes.

Depth testing

To set up depth testing

const value = model.setParameters({
depthWriteEnabled: true,
depthCompare: 'less-equal'
GL.STENCIL_TESTGLbooleanfalseEnables stencil testing
GL.STENCIL_CLEAR_VALUEGLint0Sets index used when stencil buffer is cleared.
GL.STENCIL_WRITEMASKGLuint0xFFFFFFFFSets bit mask enabling writing of individual bits in the stencil planes
GL.STENCIL_BACK_WRITEMASKGLuint0xFFFFFFFFSets bit mask enabling writing of individual bits in the stencil planes
GL.STENCIL_BACK_VALUE_MASKGLuint0xFFFFFFFFSets bit mask enabling writing of individual bits in the stencil planes
GL.STENCIL_FAILGLenumGL.KEEPstencil test fail action
GL.STENCIL_PASS_DEPTH_FAILGLenumGL.KEEPdepth test fail action
GL.STENCIL_PASS_DEPTH_PASSGLenumGL.KEEPdepth test pass action
GL.STENCIL_BACK_FAILGLenumGL.KEEPstencil test fail action, back
GL.STENCIL_BACK_PASS_DEPTH_FAILGLenumGL.KEEPdepth test fail action, back
GL.STENCIL_BACK_PASS_DEPTH_PASSGLenumGL.KEEPdepth test pass action, back


Function styleSets parameter(s)
GL.BLENDGLbooleanfalseBlending enabled
GL.BLEND_COLORFloat32Array(4)[0, 0, 0, 0]