Convert Figma logo to code with AI

greggman logotwgl.js

A Tiny WebGL helper Library

2,647
261
2,647
53

Top Related Projects

101,622

JavaScript 3D Library.

5,213

👑 Functional WebGL

3,696

Minimal WebGL Library

43,513

The HTML5 Creation Engine: Create beautiful digital content with the fastest, most flexible 2D WebGL renderer.

2,627

The Official Khronos WebGL Repository

15,084

GPU Accelerated JavaScript

Quick Overview

TWGL.js (Tiny WebGL) is a small, lightweight library for working with WebGL. It simplifies many common WebGL operations, reducing boilerplate code and making it easier to create WebGL applications. TWGL.js aims to make WebGL programming more accessible and efficient.

Pros

  • Lightweight and minimal, with a small footprint
  • Simplifies common WebGL tasks, reducing boilerplate code
  • Provides both low-level and high-level APIs for flexibility
  • Compatible with other WebGL libraries and frameworks

Cons

  • Less feature-rich compared to larger WebGL frameworks
  • May require more manual work for complex 3D scenes
  • Limited documentation compared to more popular libraries
  • Smaller community and ecosystem compared to alternatives

Code Examples

Creating a simple WebGL program:

const gl = document.querySelector('canvas').getContext('webgl');
const programInfo = twgl.createProgramInfo(gl, ['vs', 'fs']);

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);

twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.drawBufferInfo(gl, bufferInfo);

Loading a texture:

const textures = twgl.createTextures(gl, {
  myTexture: { src: 'path/to/image.jpg' },
});

// Use the texture in your shader
twgl.setUniforms(programInfo, {
  u_texture: textures.myTexture,
});

Creating a 3D object:

const arrays = twgl.primitives.createCubeVertices(1);
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
const vao = twgl.createVAOFromBufferInfo(gl, programInfo, bufferInfo);

// Render the cube
gl.bindVertexArray(vao);
twgl.drawBufferInfo(gl, bufferInfo);

Getting Started

  1. Include TWGL.js in your project:

    <script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
    
  2. Create a canvas element in your HTML:

    <canvas id="c"></canvas>
    
  3. Initialize WebGL and create a simple program:

    const gl = document.getElementById('c').getContext('webgl');
    const programInfo = twgl.createProgramInfo(gl, ['vs', 'fs']);
    
    // Your WebGL code here
    
  4. Start creating WebGL objects and rendering your scene using TWGL.js functions.

Competitor Comparisons

101,622

JavaScript 3D Library.

Pros of Three.js

  • Extensive feature set with high-level abstractions for 3D graphics
  • Large community and ecosystem with numerous examples and extensions
  • Comprehensive documentation and learning resources

Cons of Three.js

  • Larger file size and potentially higher performance overhead
  • Steeper learning curve for beginners due to its extensive API
  • May be overkill for simple WebGL projects

Code Comparison

Three.js:

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

TWGL.js:

const gl = document.createElement("canvas").getContext("webgl");
const programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
const bufferInfo = twgl.primitives.createCubeBufferInfo(gl);
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

TWGL.js is a lightweight WebGL library that provides a thin wrapper around WebGL, offering a more low-level approach compared to Three.js. It's designed for developers who want more direct control over WebGL operations while reducing boilerplate code. TWGL.js is smaller in size and potentially faster for simple projects, but lacks the high-level features and extensive ecosystem of Three.js.

5,213

👑 Functional WebGL

Pros of regl

  • Functional approach with stateless rendering, promoting cleaner and more predictable code
  • More advanced features like instancing and batch rendering built-in
  • Extensive documentation and examples

Cons of regl

  • Steeper learning curve, especially for developers familiar with imperative WebGL
  • Less flexible for low-level WebGL operations
  • Potentially higher overhead for simple rendering tasks

Code Comparison

twgl.js:

const programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.drawBufferInfo(gl, bufferInfo);

regl:

const draw = regl({
  frag: `...`,
  vert: `...`,
  attributes: { position: [/*...*/] },
  count: 3
})
draw()

Summary

regl offers a more functional and declarative approach to WebGL rendering, with built-in advanced features. It's well-suited for complex rendering tasks but may have a steeper learning curve. twgl.js provides a more traditional, lower-level API that closely mirrors WebGL, offering flexibility and ease of use for simpler tasks. The choice between them depends on the project's complexity and the developer's preferred programming paradigm.

3,696

Minimal WebGL Library

Pros of OGL

  • More comprehensive 3D engine with built-in math utilities and scene graph
  • Modern ES6+ codebase with modular architecture
  • Extensive examples and documentation

Cons of OGL

  • Larger file size and potentially higher learning curve
  • Less focus on raw WebGL abstraction compared to TWGL.js
  • May be overkill for simpler projects or those requiring fine-grained control

Code Comparison

TWGL.js (simple triangle):

const programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.drawBufferInfo(gl, bufferInfo);

OGL (simple triangle):

const geometry = new Geometry(gl, {
    position: {size: 3, data: new Float32Array([-0.5, -0.5, 0, 0.5, -0.5, 0, 0, 0.5, 0])},
});
const program = new Program(gl, {vertex, fragment});
const mesh = new Mesh(gl, {geometry, program});
renderer.render({scene: mesh});

Both libraries simplify WebGL usage, but OGL provides a higher-level abstraction with more built-in features for 3D graphics, while TWGL.js focuses on streamlining raw WebGL operations.

43,513

The HTML5 Creation Engine: Create beautiful digital content with the fastest, most flexible 2D WebGL renderer.

Pros of PixiJS

  • More comprehensive and feature-rich, offering a complete 2D rendering engine
  • Extensive documentation and large community support
  • Built-in support for sprites, textures, and complex animations

Cons of PixiJS

  • Larger file size and potentially higher performance overhead
  • Steeper learning curve due to its extensive API
  • May be overkill for simple WebGL projects

Code Comparison

TWGL.js (basic triangle rendering):

const programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
const arrays = {
  position: [-1, -1, 0, 1, -1, 0, 0, 1, 0],
};
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
twgl.drawBufferInfo(gl, bufferInfo);

PixiJS (basic sprite rendering):

const app = new PIXI.Application();
document.body.appendChild(app.view);
const sprite = PIXI.Sprite.from("image.png");
app.stage.addChild(sprite);

Summary

TWGL.js is a lightweight WebGL wrapper focused on simplifying WebGL programming, while PixiJS is a full-featured 2D rendering engine. TWGL.js is better suited for developers who want low-level WebGL control with less boilerplate, whereas PixiJS is ideal for creating complex 2D graphics and games with a higher-level API.

2,627

The Official Khronos WebGL Repository

Pros of WebGL

  • Official specification and reference implementation
  • Comprehensive documentation and examples
  • Broad industry support and standardization

Cons of WebGL

  • Lower-level API, requiring more boilerplate code
  • Steeper learning curve for beginners
  • Less abstraction for common tasks

Code Comparison

WebGL:

const gl = canvas.getContext('webgl');
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);

twgl.js:

const programInfo = twgl.createProgramInfo(gl, [vertexShaderSource, fragmentShaderSource]);

Key Differences

  • WebGL provides a low-level API for direct GPU access
  • twgl.js offers a higher-level abstraction layer on top of WebGL
  • WebGL requires more manual setup and management of WebGL state
  • twgl.js simplifies common tasks like buffer creation and shader compilation
  • WebGL gives more fine-grained control over rendering pipeline
  • twgl.js reduces boilerplate code and improves development speed

Use Cases

  • WebGL: When full control over the rendering pipeline is needed
  • twgl.js: For rapid prototyping and simpler WebGL projects
  • WebGL: In performance-critical applications requiring optimizations
  • twgl.js: When focusing on higher-level graphics concepts without diving into WebGL intricacies
15,084

GPU Accelerated JavaScript

Pros of gpu.js

  • Provides high-level abstractions for GPU computing
  • Supports both browser and Node.js environments
  • Offers automatic kernel compilation for different GPU architectures

Cons of gpu.js

  • Steeper learning curve due to its focus on GPU computing
  • May have performance overhead for simpler graphics tasks
  • Less focused on WebGL-specific optimizations

Code Comparison

twgl.js (WebGL-focused):

const programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.drawBufferInfo(gl, bufferInfo);

gpu.js (GPU computing):

const gpu = new GPU();
const kernel = gpu.createKernel(function(a, b) {
  return a[this.thread.y][this.thread.x] + b[this.thread.y][this.thread.x];
}).setOutput([width, height]);
const result = kernel(matrixA, matrixB);

twgl.js focuses on simplifying WebGL operations, while gpu.js provides a higher-level abstraction for GPU computing. twgl.js is more suitable for direct WebGL programming, whereas gpu.js excels in general-purpose GPU computations across different environments.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

TWGL: A Tiny WebGL helper Library
[rhymes with wiggle]

Build Status

This library's sole purpose is to make using the WebGL API less verbose.

TL;DR

If you want to get stuff done use three.js. If you want to do stuff low-level with WebGL consider using TWGL.

The tiniest example

Not including the shaders (which is a simple quad shader) here's the entire code

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

  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);

  function render(time) {
    twgl.resizeCanvasToDisplaySize(gl.canvas);
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

    const uniforms = {
      time: time * 0.001,
      resolution: [gl.canvas.width, gl.canvas.height],
    };

    gl.useProgram(programInfo.program);
    twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
    twgl.setUniforms(programInfo, uniforms);
    twgl.drawBufferInfo(gl, bufferInfo);

    requestAnimationFrame(render);
  }
  requestAnimationFrame(render);
</script>

And here it is live.

Why? What? How?

WebGL is a very verbose API. Setting up shaders, buffers, attributes and uniforms takes a lot of code. A simple lit cube in WebGL might easily take over 60 calls into WebGL.

At its core there's really only a few main functions

  • twgl.createProgramInfo compiles a shader and creates setters for attribs and uniforms
  • twgl.createBufferInfoFromArrays creates buffers and attribute settings
  • twgl.setBuffersAndAttributes binds buffers and sets attributes
  • twgl.setUniforms sets the uniforms
  • twgl.createTextures creates textures of various sorts
  • twgl.createFramebufferInfo creates a framebuffer and attachments.

There's a few extra helpers and lower-level functions if you need them but those 6 functions are the core of TWGL.

Compare the TWGL vs WebGL code for a point lit cube.

Compiling a Shader and looking up locations

TWGL

const programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);

WebGL

// Note: I'm conceding that you'll likely already have the 30 lines of
// code for compiling GLSL
const program = twgl.createProgramFromScripts(gl, ["vs", "fs"]);

const u_lightWorldPosLoc = gl.getUniformLocation(program, "u_lightWorldPos");
const u_lightColorLoc = gl.getUniformLocation(program, "u_lightColor");
const u_ambientLoc = gl.getUniformLocation(program, "u_ambient");
const u_specularLoc = gl.getUniformLocation(program, "u_specular");
const u_shininessLoc = gl.getUniformLocation(program, "u_shininess");
const u_specularFactorLoc = gl.getUniformLocation(program, "u_specularFactor");
const u_diffuseLoc = gl.getUniformLocation(program, "u_diffuse");
const u_worldLoc = gl.getUniformLocation(program, "u_world");
const u_worldInverseTransposeLoc = gl.getUniformLocation(program, "u_worldInverseTranspose");
const u_worldViewProjectionLoc = gl.getUniformLocation(program, "u_worldViewProjection");
const u_viewInverseLoc = gl.getUniformLocation(program, "u_viewInverse");

const positionLoc = gl.getAttribLocation(program, "a_position");
const normalLoc = gl.getAttribLocation(program, "a_normal");
const texcoordLoc = gl.getAttribLocation(program, "a_texcoord");

Creating Buffers for a Cube

TWGL

const arrays = {
  position: [1,1,-1,1,1,1,1,-1,1,1,-1,-1,-1,1,1,-1,1,-1,-1,-1,-1,-1,-1,1,-1,1,1,1,1,1,1,1,-1,-1,1,-1,-1,-1,-1,1,-1,-1,1,-1,1,-1,-1,1,1,1,1,-1,1,1,-1,-1,1,1,-1,1,-1,1,-1,1,1,-1,1,-1,-1,-1,-1,-1],
  normal:   [1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1],
  texcoord: [1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1],
  indices:  [0,1,2,0,2,3,4,5,6,4,6,7,8,9,10,8,10,11,12,13,14,12,14,15,16,17,18,16,18,19,20,21,22,20,22,23],
};
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

WebGL

const positions = [1,1,-1,1,1,1,1,-1,1,1,-1,-1,-1,1,1,-1,1,-1,-1,-1,-1,-1,-1,1,-1,1,1,1,1,1,1,1,-1,-1,1,-1,-1,-1,-1,1,-1,-1,1,-1,1,-1,-1,1,1,1,1,-1,1,1,-1,-1,1,1,-1,1,-1,1,-1,1,1,-1,1,-1,-1,-1,-1,-1];
const normals   = [1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1];
const texcoords = [1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1];
const indices   = [0,1,2,0,2,3,4,5,6,4,6,7,8,9,10,8,10,11,12,13,14,12,14,15,16,17,18,16,18,19,20,21,22,20,22,23];

const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
const normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
const texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texcoords), gl.STATIC_DRAW);
const indicesBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);

Setting Attributes and Indices for a Cube

TWGL

twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);

WebGL

gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(positionLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.vertexAttribPointer(normalLoc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(normalLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.vertexAttribPointer(texcoordLoc, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(texcoordLoc);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);

Setting Uniforms for a Lit Cube

TWGL

// At Init time
const uniforms = {
  u_lightWorldPos: [1, 8, -10],
  u_lightColor: [1, 0.8, 0.8, 1],
  u_ambient: [0, 0, 0, 1],
  u_specular: [1, 1, 1, 1],
  u_shininess: 50,
  u_specularFactor: 1,
  u_diffuse: tex,
};

// At render time
uniforms.u_viewInverse = camera;
uniforms.u_world = world;
uniforms.u_worldInverseTranspose = m4.transpose(m4.inverse(world));
uniforms.u_worldViewProjection = m4.multiply(viewProjection, world);

twgl.setUniforms(programInfo, uniforms);

WebGL

// At Init time
const u_lightWorldPos = [1, 8, -10];
const u_lightColor = [1, 0.8, 0.8, 1];
const u_ambient = [0, 0, 0, 1];
const u_specular = [1, 1, 1, 1];
const u_shininess = 50;
const u_specularFactor = 1;
const u_diffuse = 0;

// At render time
gl.uniform3fv(u_lightWorldPosLoc, u_lightWorldPos);
gl.uniform4fv(u_lightColorLoc, u_lightColor);
gl.uniform4fv(u_ambientLoc, u_ambient);
gl.uniform4fv(u_specularLoc, u_specular);
gl.uniform1f(u_shininessLoc, u_shininess);
gl.uniform1f(u_specularFactorLoc, u_specularFactor);
gl.uniform1i(u_diffuseLoc, u_diffuse);
gl.uniformMatrix4fv(u_viewInverseLoc, false, camera);
gl.uniformMatrix4fv(u_worldLoc, false, world);
gl.uniformMatrix4fv(u_worldInverseTransposeLoc, false, m4.transpose(m4.inverse(world)));
gl.uniformMatrix4fv(u_worldViewProjectionLoc, false, m4.multiply(viewProjection, world));

Loading / Setting up textures

TWGL

const textures = twgl.createTextures(gl, {
  // a power of 2 image
  hftIcon: { src: "images/hft-icon-16.png", mag: gl.NEAREST },
  // a non-power of 2 image
  clover: { src: "images/clover.jpg" },
  // From a canvas
  fromCanvas: { src: ctx.canvas },
  // A cubemap from 6 images
  yokohama: {
    target: gl.TEXTURE_CUBE_MAP,
    src: [
      'images/yokohama/posx.jpg',
      'images/yokohama/negx.jpg',
      'images/yokohama/posy.jpg',
      'images/yokohama/negy.jpg',
      'images/yokohama/posz.jpg',
      'images/yokohama/negz.jpg',
    ],
  },
  // A cubemap from 1 image (can be 1x6, 2x3, 3x2, 6x1)
  goldengate: {
    target: gl.TEXTURE_CUBE_MAP,
    src: 'images/goldengate.jpg',
  },
  // A 2x2 pixel texture from a JavaScript array
  checker: {
    mag: gl.NEAREST,
    min: gl.LINEAR,
    src: [
      255,255,255,255,
      192,192,192,255,
      192,192,192,255,
      255,255,255,255,
    ],
  },
  // a 1x8 pixel texture from a typed array.
  stripe: {
    mag: gl.NEAREST,
    min: gl.LINEAR,
    format: gl.LUMINANCE,
    src: new Uint8Array([
      255,
      128,
      255,
      128,
      255,
      128,
      255,
      128,
    ]),
    width: 1,
  },
});

WebGL

// Let's assume I already loaded all the images

// a power of 2 image
const hftIconTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, hftIconImg);
gl.generateMipmaps(gl.TEXTURE_2D);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
// a non-power of 2 image
const cloverTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, hftIconImg);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
// From a canvas
const cloverTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, ctx.canvas);
gl.generateMipmaps(gl.TEXTURE_2D);
// A cubemap from 6 images
const yokohamaTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_CUBE_MAP, tex);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, posXImg);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, negXImg);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Y, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, posYImg);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, negYImg);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Z, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, posZImg);
gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, negZImg);
gl.generateMipmaps(gl.TEXTURE_CUBE_MAP);
// A cubemap from 1 image (can be 1x6, 2x3, 3x2, 6x1)
const goldengateTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_CUBE_MAP, tex);
const size = goldengate.width / 3;  // assume it's a 3x2 texture
const slices = [0, 0, 1, 0, 2, 0, 0, 1, 1, 1, 2, 1];
const tempCtx = document.createElement("canvas").getContext("2d");
tempCtx.canvas.width = size;
tempCtx.canvas.height = size;
for (let ii = 0; ii < 6; ++ii) {
  const xOffset = slices[ii * 2 + 0] * size;
  const yOffset = slices[ii * 2 + 1] * size;
  tempCtx.drawImage(element, xOffset, yOffset, size, size, 0, 0, size, size);
  gl.texImage2D(faces[ii], 0, format, format, type, tempCtx.canvas);
}
gl.generateMipmaps(gl.TEXTURE_CUBE_MAP);
// A 2x2 pixel texture from a JavaScript array
const checkerTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([
    255,255,255,255,
    192,192,192,255,
    192,192,192,255,
    255,255,255,255,
  ]));
gl.generateMipmaps(gl.TEXTURE_2D);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// a 1x8 pixel texture from a typed array.
const stripeTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, 1, 8, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, new Uint8Array([
    255,
    128,
    255,
    128,
    255,
    128,
    255,
    128,
  ]));
gl.generateMipmaps(gl.TEXTURE_2D);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

Creating Framebuffers and attachments

TWGL

const attachments = [
  { format: RGBA, type: UNSIGNED_BYTE, min: LINEAR, wrap: CLAMP_TO_EDGE },
  { format: DEPTH_STENCIL, },
];
const fbi = twgl.createFramebufferInfo(gl, attachments);

WebGL

const fb = gl.createFramebuffer(gl.FRAMEBUFFER);
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.drawingBufferWidth, gl.drawingBufferHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
const rb = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, rb);

Setting uniform and uniformblock structures and arrays

Given an array of GLSL structures like this

struct Light {
  float intensity;
  float shininess;
  vec4 color;
}
uniform Light lights[2];

TWGL

const progInfo = twgl.createProgramInfo(gl, [vs, fs]);
...
twgl.setUniforms(progInfo, {
  lights: [
    { intensity: 5.0, shininess: 100, color: [1, 0, 0, 1] },
    { intensity: 2.0, shininess:  50, color: [0, 0, 1, 1] },
  ],
});

WebGL

// assuming we already compiled and linked the program
const light0IntensityLoc = gl.getUniformLocation('lights[0].intensity');
const light0ShininessLoc = gl.getUniformLocation('lights[0].shininess');
const light0ColorLoc = gl.getUniformLocation('lights[0].color');
const light1IntensityLoc = gl.getUniformLocation('lights[1].intensity');
const light1ShininessLoc = gl.getUniformLocation('lights[1].shininess');
const light1ColorLoc = gl.getUniformLocation('lights[1].color');
...
gl.uniform1f(light0IntensityLoc, 5.0);
gl.uniform1f(light0ShininessLoc, 100);
gl.uniform4fv(light0ColorLoc, [1, 0, 0, 1]);
gl.uniform1f(light1IntensityLoc, 2.0);
gl.uniform1f(light1ShininessLoc, 50);
gl.uniform4fv(light1ColorLoc, [0, 0, 1, 1]);

If you just want to set the 2nd light in TWGL you can do this

const progInfo = twgl.createProgramInfo(gl, [vs, fs]);
...
twgl.setUniforms(progInfo, {
  'lights[1]': { intensity: 5.0, shininess: 100, color: [1, 0, 0, 1] },
});

Compare

TWGL example vs WebGL example

Examples

WebGL 2 Examples

OffscreenCanvas Example

ES6 module support

AMD support

CommonJS / Browserify support

Other Features

  • Includes some optional 3d math functions (full version)

    You are welcome to use any math library as long as it stores matrices as flat Float32Array or JavaScript arrays.

  • Includes some optional primitive generators (full version)

    planes, cubes, spheres, ... Just to help get started

Usage

See the examples. Otherwise there's a few different versions

  • twgl-full.module.js the es6 module version
  • twgl-full.min.js the minified full version
  • twgl-full.js the concatenated full version
  • twgl.min.js the minimum version (no 3d math, no primitives)
  • twgl.js the concatenated minimum version (no 3d math, no primitives)

Download

  • from github

    http://github.com/greggman/twgl.js

  • from bower

    bower install twgl.js
    
  • from npm

    npm install twgl.js
    

    or

    npm install twgl-base.js
    
  • from git

    git clone https://github.com/greggman/twgl.js.git
    

Rationale and other chit-chat

TWGL's is an attempt to make WebGL simpler by providing a few tiny helper functions that make it much less verbose and remove the tedium. TWGL is NOT trying to help with the complexity of managing shaders and writing GLSL. Nor is it a 3D library like three.js. It's just trying to make WebGL less verbose.

TWGL can be considered a spiritual successor to TDL. Where as TDL created several classes that wrapped WebGL, TWGL tries not to wrap anything. In fact you can manually create nearly all TWGL data structures.

For example the function setAttributes takes an object of attributes. In WebGL you might write code like this

gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(positionLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.vertexAttribPointer(normalLoc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(normalLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.vertexAttribPointer(texcoordLoc, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(texcoordLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, colorsBuffer);
gl.vertexAttribPointer(colorLoc, 4, gl.UNSIGNED_BYTE, true, 0, 0);
gl.enableVertexAttribArray(colorLoc);

setAttributes is just the simplest code to do that for you.

// make attributes for TWGL manually
const attribs = {
  a_position: { buffer: positionBuffer, size: 3, },
  a_normal:   { buffer: normalBuffer,   size: 3, },
  a_texcoord: { buffer: texcoordBuffer, size: 2, },
  a_color:    { buffer: colorBuffer,    size: 4, type: gl.UNSIGNED_BYTE, normalize: true, },
};
twgl.setAttributes(attribSetters, attribs);

The point of the example above is TWGL is a thin wrapper. All it's doing is trying to make common WebGL operations easier and less verbose. Feel free to mix it with raw WebGL.

API Docs

API Docs are here.

Want to learn WebGL?

Try webglfundamentals.org

NPM DownloadsLast 30 Days