# TWGL.js: the WebGL helper that keeps you close to the raw API

> TWGL.js cuts the boilerplate out of WebGL without hiding the API. It is for engineers who want to write GLSL and manage buffers themselves, but not by hand.

**greggman/twgl.js** — A Tiny WebGL helper Library

- Repository: https://github.com/greggman/twgl.js
- Website: http://twgljs.org
- Stars: 3,004 · Forks: 261
- Language: JavaScript
- License: MIT
- Published: 2026-09-24 · Updated: 2026-09-24 · Language: en
- Canonical page: https://hysenlabs.com/projects/greggman-twgl-js

## The boilerplate TWGL.js removes, and the audience it assumes

WebGL is verbose in a specific, repetitive way. The README states that a simple lit cube might easily take over 60 calls into WebGL. Most of those calls are not decisions about your scene; they are bookkeeping. You compile a shader, then ask the driver where each uniform lives, then ask again for each attribute, then bind buffers, then point attributes at them, then upload uniforms one by one. TWGL.js exists to collapse that bookkeeping.

The audience is narrow on purpose. The README opens with a directive rather than a pitch: if you want to get stuff done, use three.js; if you want to do stuff low-level with WebGL, consider TWGL. That is the whole positioning. TWGL.js is not a renderer. It has no scene graph, no camera abstraction that draws for you, no material system. It assumes you already know what a vertex attribute is and you have opinions about your draw order. What it removes is the part where you retype gl.getAttribLocation for the fourth time.

## Six functions carry the library

The README names the core surface directly: createProgramInfo, createBufferInfoFromArrays, setBuffersAndAttributes, setUniforms, createTextures and createFramebufferInfo. Everything else is described as extra helpers and lower-level functions.

The mechanism is worth understanding because it explains both the convenience and the failure mode. createProgramInfo compiles a shader and, in the same pass, creates setters for the attributes and uniforms it finds in that program. That is why the uniform object in the tiny example can use plain keys like time and resolution: the setter functions were generated from the compiled program, not looked up at every frame. createBufferInfoFromArrays takes a plain object of JavaScript arrays, uploads each one as a buffer, and records the attribute settings alongside it. setBuffersAndAttributes then replays those settings in one call. The data flow is: arrays in, buffer info out, buffer info plus program info in, draw call out.

The tiny example in the README shows the whole loop. It creates a program info from two shader script ids, builds a buffer info from a position array, and then on each frame resizes the canvas, sets the viewport, fills a uniforms object, and calls setBuffersAndAttributes, setUniforms and drawBufferInfo in sequence. The render function is roughly ten lines. The equivalent hand-written version is not.

## Installing TWGL.js and drawing a first quad

The package is published as twgl.js with version 7.0.1 in package.json, and the package files list points at dist/7.x/twgl-full.js, dist/7.x/twgl.js and their TypeScript declaration files. The README's smallest example loads a browser build directly from a script tag, which is the fastest way to confirm the library works before wiring a bundler.

```html
<canvas id="c"></canvas>
<script src="../dist/7.x/twgl-full.min.js"></script>
<script>
  const gl = document.getElementById("c").getContext("webgl");
  const programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
</script>
```

The two strings passed to createProgramInfo are the ids of script elements holding your vertex and fragment shaders. After this runs, programInfo.program is a usable WebGLProgram and programInfo holds the generated setters. If a shader fails to compile, this is the call that surfaces it.

Next, describe the geometry as plain arrays and let TWGL build the buffers. The README uses a full cube, but the shape of the call is the same for anything.

```javascript
const arrays = {
  position: [-1, -1, 0, 1, -1, 0, -1, 1, 0, -1, 1, 0, 1, -1, 0, 1, 1, 0],
};
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
```

The keys of the arrays object are matched against attribute names in your shader. Then the render loop is three calls plus your own state setup.

```javascript
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, { time: time * 0.001 });
twgl.drawBufferInfo(gl, bufferInfo);
```

drawBufferInfo reads the index count and mode recorded in the buffer info, so you do not pass a count yourself. The live version of this example is linked from the README as examples/tiny.html.

## Where TWGL.js stops helping

The generated setters are the library's main convenience and its sharpest edge. Because createProgramInfo builds setters from the program it compiled, those setters are tied to that program's uniform names. If you rename a uniform in GLSL and forget to rename the key in your uniforms object, nothing throws at the call site. The setter for the old name does not exist, so the value is simply never uploaded, and the visual result is a silently wrong frame. That is a debugging session spent staring at a shader that looks correct.

The same applies to attributes. If an attribute name in your shader does not match a key in the arrays object passed to createBufferInfoFromArrays, that attribute is not fed. There is no validation layer mentioned in the README that would catch the mismatch for you.

There is also no scene graph, and that is a design boundary rather than a defect. If you have forty objects with materials, transforms and parent-child relationships, you will write the traversal and the draw ordering yourself. TWGL.js gives you a shorter way to issue each draw, not a shorter way to decide which draws to issue. Teams that want the second thing should read the README's own advice and use three.js.

## TWGL.js against three.js: a helper versus a renderer

The README names three.js as the alternative, and the difference in approach is structural rather than a matter of feature lists. Three.js owns the frame. You build a scene graph, attach meshes with materials, add lights, and call render; the library decides what to draw, in what order, and with which shader. Its material system generates GLSL for you in most cases.

TWGL.js inverts that. You own the frame. You compile your own shaders, you decide the draw order, and you call the draw functions yourself. What the library owns is the repetitive translation layer between your JavaScript data and the WebGL calls. That is why the README's comparison sections are written as side-by-side call counts: one line of TWGL against eleven getUniformLocation calls, one setBuffersAndAttributes against eight lines of buffer binding and vertexAttribPointer.

The practical consequence is that TWGL.js does not scale by adding abstractions; it scales by staying small while you add your own. If your project's value is in custom shader work and unusual draw patterns, that is the right shape. If your project's value is in getting a lit, textured scene on screen this week, the renderer wins.

## Maintenance, distribution and the MIT licence

The repository is not archived, and the last push was on 2026-09-09. The package.json shows a version of 7.0.1 and a build pipeline driven by Grunt, with npm scripts for lint, build, test, buildts and builddocs. Tests run in two parts: testts runs tsc against test/src/ts/, and testbrowser drives a browser through test/puppeteer.js. TypeScript declarations ship in the published files, so typed consumers get types without a separate @types package.

Upgrade cost is mostly a question of which artifact you consume. The published files list five entries under dist/7.x, and the package exposes main, module and types pointing at twgl-full variants. If you import the full build you get the whole surface; the non-full twgl.js is the smaller option. The major version is baked into the dist path, so a future major release would land under a different directory rather than overwriting the one you depend on. That is a quiet but real convenience for anyone pinning a script tag.

The licence is MIT, stated in package.json and present as LICENSE.md at the repository root. MIT permits commercial and closed-source use and requires the copyright notice and permission notice to be included. That is the general shape of the terms; read LICENSE.md for the actual text, since this is a description of the licence and not legal advice.

## Conclusion

Adopt TWGL.js if you are comfortable writing shaders and want to stop hand-writing getUniformLocation and vertexAttribPointer calls, and you do not want a scene graph deciding how your draw calls are ordered. Do not adopt it if you need a renderer, a material system or a glTF loader; the README points that audience at three.js instead. Before committing, check the dist/7.x files your bundler actually resolves, and confirm that the attribute and uniform names in your shaders match the keys TWGL auto-detects.

## FAQ

### What is TWGL.js used for?

It is a WebGL helper library whose stated purpose is making the WebGL API less verbose. It compiles shaders, creates buffers and textures, and sets attributes and uniforms, while leaving scene management and draw ordering to you.

### How do I install TWGL.js?

The package is published as twgl.js, currently at version 7.0.1, with main, module and types entries under dist/7.x. The README's smallest example instead loads dist/7.x/twgl-full.min.js directly with a script tag.

### Does TWGL.js replace three.js?

No. The README says that if you want to get stuff done you should use three.js, and that TWGL is for doing stuff low-level with WebGL. TWGL has no scene graph or material system; it shortens individual WebGL calls.

### Which functions are the core of TWGL.js?

The README lists six: createProgramInfo, createBufferInfoFromArrays, setBuffersAndAttributes, setUniforms, createTextures and createFramebufferInfo. Everything else is described as extra helpers and lower-level functions.

## Sources

- [greggman/twgl.js on GitHub](https://github.com/greggman/twgl.js)
- [Issues](https://github.com/greggman/twgl.js/issues)
- [License: MIT](https://github.com/greggman/twgl.js/blob/main/LICENSE)
- [Project website](http://twgljs.org)
- [README](https://github.com/greggman/twgl.js/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/greggman-twgl-js
