Examples from "WebGL Programming Guide" on JSFiddle

I moved all examples from WebGL Programming Guide to JSFiddle

Chapter 02. Your First Step with WebGL
ch02/HelloCanvas: ch02/HelloCanvas - JSFiddle - Code Playground
ch02/HelloPoint1: ch02/HelloPoint1 - JSFiddle - Code Playground
ch02/HelloPoint2: ch02/HelloPoint2 - JSFiddle - Code Playground
ch02/ClickedPoints: ch02/ClickedPoints - JSFiddle - Code Playground
ch02/ColoredPoints: ch02/ColoredPoints - JSFiddle - Code Playground

Chapter 03. Drawing and Transforming Triangles
ch03/MultiPoint: ch03/MultiPoint - JSFiddle - Code Playground
ch03/HelloTriangle: ch03/HelloTriangle - JSFiddle - Code Playground
ch03/HelloQuad: ch03/HelloQuad - JSFiddle - Code Playground
ch03/HelloQuad_FAN: ch03/HelloQuad_FAN - JSFiddle - Code Playground
ch03/HelloTriangle_LINES: ch03/HelloTriangle_LINES - JSFiddle - Code Playground
ch03/HelloTriangle_LINE_STRIP: ch03/HelloTriangle_LINE_STRIP - JSFiddle - Code Playground
ch03/HelloTriangle_LINE_LOOP: ch03/HelloTriangle_LINE_LOOP - JSFiddle - Code Playground
ch03/TranslatedTriangle: ch03/TranslatedTriangle - JSFiddle - Code Playground
ch03/RotatedTriangle: ch03/RotatedTriangle - JSFiddle - Code Playground
ch03/RotatedTriangle_Matrix: ch03/RotatedTriangle_Matrix - JSFiddle - Code Playground
ch03/ScaledTriangle_Matrix: ch03/ScaledTriangle_Matrix - JSFiddle - Code Playground

Chapter 04. More Transformations and Basic Animation
ch04/RotatedTriangle_Matrix4: ch04/RotatedTriangle_Matrix4 - JSFiddle - Code Playground
ch04/RotatedTranslatedTriangle: ch04/RotatedTranslatedTriangle - JSFiddle - Code Playground
ch04/TranslatedRotatedTriangle: ch04/TranslatedRotatedTriangle - JSFiddle - Code Playground
ch04/RotatingTriangle: ch04/RotatingTriangle - JSFiddle - Code Playground
ch04/RotatingTranslatedTriangle: ch04/RotatingTranslatedTriangle - JSFiddle - Code Playground
ch04/RotatingTriangle_withButtons: ch04/RotatingTriangle_withButtons - JSFiddle - Code Playground

Chapter 05. Using Colors and Texture Images
ch05/MultiAttributeSize: ch05/MultiAttributeSize - JSFiddle - Code Playground
ch05/MultiAttributeSize_Interleaved: ch05/MultiAttributeSize_Interleaved - JSFiddle - Code Playground
ch05/MultiAttributeColor: ch05/MultiAttributeColor - JSFiddle - Code Playground
ch05/ColoredTriangle: ch05/ColoredTriangle - JSFiddle - Code Playground
ch05/HelloTriangle_FragCoord: ch05/HelloTriangle_FragCoord - JSFiddle - Code Playground
ch05/TexturedQuad: ch05/TexturedQuad - JSFiddle - Code Playground
ch05/TexturedQuad_Repeat: ch05/TexturedQuad_Repeat - JSFiddle - Code Playground
ch05/TexturedQuad_Clamp_Mirror: ch05/TexturedQuad_Clamp_Mirror - JSFiddle - Code Playground
ch05/MultiTexture: ch05/MultiTexture - JSFiddle - Code Playground

Chapter 07. Toward the 3D World
ch07/LookAtTriangles: ch07/LookAtTriangles - JSFiddle - Code Playground
ch07/LookAtRotatedTriangles: ch07/LookAtRotatedTriangles - JSFiddle - Code Playground
ch07/LookAtRotatedTriangles_modelViewMatrix: ch07/LookAtRotatedTriangles_modelViewMatrix - JSFiddle - Code Playground
ch07/LookAtTrianglesWithKeys: ch07/LookAtTrianglesWithKeys - JSFiddle - Code Playground
ch07/OrthoView: ch07/OrthoView - JSFiddle - Code Playground
ch07/LookAtTrianglesWithKey_ViewVolume: ch07/LookAtTrianglesWithKey_ViewVolume - JSFiddle - Code Playground
ch07/OrthoView_halfSize: ch07/OrthoView_halfSize - JSFiddle - Code Playground
ch07/OrthoView_halfWidth: ch07/OrthoView_halfWidth - JSFiddle - Code Playground
ch07/PerspectiveView: ch07/PerspectiveView - JSFiddle - Code Playground
ch07/PerspectiveView_mvp: ch07/PerspectiveView_mvp - JSFiddle - Code Playground
ch07/PerspectiveView_mvpMatrix: ch07/PerspectiveView_mvpMatrix - JSFiddle - Code Playground
ch07/DepthBuffer: ch07/DepthBuffer - JSFiddle - Code Playground
ch07/Zfighting: ch07/Zfighting - JSFiddle - Code Playground
ch07/HelloCube: ch07/HelloCube - JSFiddle - Code Playground
ch07/ColoredCube: ch07/ColoredCube - JSFiddle - Code Playground
ch07/ColoredCube_singleColor: ch07/ColoredCube_singleColor - JSFiddle - Code Playground

Chapter 08. Lighting Objects
ch08/LightedCube: ch08/LightedCube - JSFiddle - Code Playground
ch08/LightedCube_animation: ch08/LightedCube_animation - JSFiddle - Code Playground
ch08/LightedCube_ambient: ch08/LightedCube_ambient - JSFiddle - Code Playground
ch08/LightedTranslatedRotatedCube: https://jsfiddle.net/8Observer8/pa88ujjg/
ch08/PointLightedCube: https://jsfiddle.net/8Observer8/vuq118ue/
ch08/PointLightedCube_animation: https://jsfiddle.net/8Observer8/5bj39hb8/
ch08/PointLightedSphere: https://jsfiddle.net/8Observer8/edz9Lz8f/
ch08/PointLightedSphere_perFragment: https://jsfiddle.net/8Observer8/qzwyow4j/
ch08/PointLightedCube_perFragment: https://jsfiddle.net/8Observer8/8t1umamf/
ch08/LightedCube_perFragment: https://jsfiddle.net/8Observer8/471y2t84/

Chapter 09. Hierarchical Objects
ch09/JointModel: https://jsfiddle.net/8Observer8/vqse5egz/
ch09/MultiJointModel: https://jsfiddle.net/8Observer8/sL53wkn3/
ch09/MultiJointModel_segment: https://jsfiddle.net/8Observer8/ygvk7odv/

Chapter 10. Advanced Techniques
ch10/RotateObject: https://jsfiddle.net/8Observer8/1f5hLmff/
ch10/PickObject: https://jsfiddle.net/8Observer8/owue624n/
ch10/PickFace: https://jsfiddle.net/8Observer8/edvw6z90/
ch10/HUD: https://jsfiddle.net/8Observer8/fLxxxs35/
ch10/3DoverWeb: https://jsfiddle.net/8Observer8/tbowcc16/
ch10/Fog: https://jsfiddle.net/8Observer8/6yf9L399/
ch10/Fog_w: https://jsfiddle.net/8Observer8/8aLvthc3/
ch10/RoundedPoints: https://jsfiddle.net/8Observer8/sjs5kmn4/
ch10/LookAtBlendedTriangles: https://jsfiddle.net/8Observer8/apoz294n/
ch10/BlendedCube: https://jsfiddle.net/8Observer8/xsrL2fs5/
ch10/ProgramObject: https://jsfiddle.net/8Observer8/jnd0j6w0/
ch10/FramebufferObject: https://jsfiddle.net/8Observer8/vaLq6d66/
ch10/Shadow: https://jsfiddle.net/8Observer8/jsnfwcae/
ch10/Shadow_highp: https://jsfiddle.net/8Observer8/brjzr00n/
ch10/Shadow_highp_sphere: https://jsfiddle.net/8Observer8/4fmyLy5f/
ch10/OBJViewer: https://jsfiddle.net/8Observer8/pws1x7uv/
ch10/RotatingTriangle_contextLost: https://jsfiddle.net/8Observer8/vs01s8Lz/

Gifts
gifts/Particle: https://jsfiddle.net/8Observer8/Ltzt31vk/
gifts/Printf: https://jsfiddle.net/8Observer8/qsw7jtec/
gifts/SpecularCube: https://jsfiddle.net/8Observer8/z4xj9rbv/
gifts/TextTexture: https://jsfiddle.net/8Observer8/qt7q2kuf/
gifts/ThreeDUI: https://jsfiddle.net/8Observer8/zdw1f2st/
gifts/Wave: https://jsfiddle.net/8Observer8/eL9odthz/
gifts/WorldCoordinateSystem: https://jsfiddle.net/8Observer8/6utj3hnk/
appendix/CoordinateSystem: https://jsfiddle.net/8Observer8/dzz056jt/

Appendix
appendix/CoordinateSystem_viewVolume: https://jsfiddle.net/8Observer8/apxLww1q/
appendix/LoadShaderFromFiles: https://jsfiddle.net/8Observer8/wdn9ubhj/

Port of first example to TypeScript.

npm i typescript -g
npm init -y
npm i -D @types/requirejs

Chapter 02. Your First Step with WebGL
ch02/HelloCanvas: https://plnkr.co/edit/8XyJQXJEkoOR8u2L?preview

public/index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Chapter 001. Example 001. Hello Canvas</title>

    <script data-main="js/RequireConfig"
        src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script>
</head>

<body>
    <canvas id="renderCanvas" width="400" height="400">
        Please use a browser that supports "canvas"
    </canvas>
</body>

</html>

src/main.ts


function main()
{
    // Retrieve <canvas> element
    const canvas = document.getElementById("renderCanvas") as HTMLCanvasElement;

    // Get the rendering context for WebGL
    const gl = canvas.getContext("webgl");
    if (!gl)
    {
        console.log("Failed to get the rendering context for WebGL");
        return;
    }

    // Set clear color
    gl.clearColor(0.0, 0.0, 0.0, 1.0);

    // Clear <canvas>
    gl.clear(gl.COLOR_BUFFER_BIT);
}

main();

src/RequireConfig.ts

requirejs.config({
    "baseUrl": "js"
});

requirejs(["main"], () => { });

tsconfig.json

{
    "compilerOptions": {
        "target": "ES5",
        "module": "AMD",
        "sourceMap": true,
        "outDir": "public/js",
        "types": [
            "requirejs"
        ]
    },
    "include": [
        "src/**/*.ts"
    ]
}

I made a demo with simple skeletal animation imported from Blender. The demo is written in TypeScript using pure WebGL 1.0. I import 3D models and animations from .dae (COLLADA) format from Blender 3D editor. No code yet, just a demo.

Run demo in browser

anim-with-correction

  • Made an object selection with a mouse click using the color ID in the shader
  • Connected the physics engine Ammo.js (Ammo.js is a port of the physical C ++ - Bullet engine)
  • Made the camera rotate while holding the mouse wheel
  • Made the camera zoom in and out by rotating the mouse wheel
  • Made Skybox (sky environment)
  • Display text that is not pixilated when zooming in. To do this, I use Distance Field from the Hiero program, as shown in the tutorial from ThinMatrix:

I used this tutorial for skeleton animation:

Loading a cube from gltf 2.0. WebGL, JavaScript

WebGL-demo in sandbox

image

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Loading a cube from gltf 2.0. WebGL, JavaScript</title>
    <script src="https://cdn.jsdelivr.net/npm/gl-matrix@3.4.3/gl-matrix-min.js"></script>
</head>

<body>
    <canvas id="renderCanvas" width="400" height="400"></canvas>
    <script>
        loadFile("assets/BoxBlender3.gltf", (content) =>
        {
            const gltf = JSON.parse(content);

            loadBin("assets/BoxBlender3.bin", (binData) =>
            {
                const canvas = document.getElementById("renderCanvas");
                const gl = canvas.getContext("webgl");

                gl.enable(gl.DEPTH_TEST);

                const vertShaderSource =
                    `attribute vec4 aPosition;
                    attribute vec4 aNormal;
                    uniform mat4 uMvpMatrix;
                    uniform mat4 uModelMatrix;
                    uniform mat4 uNormalMatrix;
                    varying vec3 vPosition;
                    varying vec3 vNormal;
                    void main()
                    {
                        gl_Position = uMvpMatrix * aPosition;
                        vPosition = vec3(uModelMatrix * aPosition);
                        vNormal = normalize(vec3(uNormalMatrix * aNormal));
                    }`;

                const fragShaderSource =
                    `precision mediump float;
                    const vec3 lightColor = vec3(1.0, 1.0, 1.0);
                    const vec3 ambientLight = vec3(0.2, 0.2, 0.2);
                    uniform vec3 uLightPosition;
                    varying vec3 vPosition;
                    varying vec3 vNormal;
                    void main()
                    {
                        vec4 color = vec4(0.5, 1.0, 0.5, 1.0);
                        vec3 normal = normalize(vNormal);
                        vec3 lightDirection = normalize(uLightPosition - vPosition);
                        float nDotL = max(dot(lightDirection, normal), 0.0);
                        vec3 diffuse = lightColor * color.rgb * nDotL;
                        vec3 ambient = ambientLight * color.rgb;
                        gl_FragColor = vec4(diffuse + ambient, color.a);
                    }`;

                const vShader = gl.createShader(gl.VERTEX_SHADER);
                gl.shaderSource(vShader, vertShaderSource);
                gl.compileShader(vShader);
                let ok = gl.getShaderParameter(vShader, gl.COMPILE_STATUS);
                if (!ok) { console.log("vert: " + gl.getShaderInfoLog(vShader)); };

                const fShader = gl.createShader(gl.FRAGMENT_SHADER);
                gl.shaderSource(fShader, fragShaderSource);
                gl.compileShader(fShader);
                ok = gl.getShaderParameter(vShader, gl.COMPILE_STATUS);
                if (!ok) { console.log("frag: " + gl.getShaderInfoLog(fShader)); };

                const program = gl.createProgram();
                gl.attachShader(program, vShader);
                gl.attachShader(program, fShader);
                gl.linkProgram(program);
                ok = gl.getProgramParameter(program, gl.LINK_STATUS);
                if (!ok) { console.log("link: " + gl.getProgramInfoLog(program)); };
                gl.useProgram(program);

                // Create a cube
                //    v6----- v5
                //   /|      /|
                //  v1------v0|
                //  | |     | |
                //  | |v7---|-|v4
                //  |/      |/
                //  v2------v3

                // const vertPositions = [
                //     0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, // v0-v1-v2-v3 front
                //     0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, // v0-v3-v4-v5 right
                //     0.5, 0.5, 0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, // v0-v5-v6-v1 up
                //     -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, // v1-v6-v7-v2 left
                //     -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, // v7-v4-v3-v2 down
                //     0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5 // v4-v7-v6-v5 back
                // ];
                const vertPosBuffer = gl.createBuffer();
                gl.bindBuffer(gl.ARRAY_BUFFER, vertPosBuffer);
                // gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertPositions), gl.STATIC_DRAW);
                // const vertPosData = new Uint8Array(binData, 0, 288);
                const vertPosData = new Float32Array(binData, 0, 288 / Float32Array.BYTES_PER_ELEMENT);
                gl.bufferData(gl.ARRAY_BUFFER, vertPosData, gl.STATIC_DRAW);
                gl.bindAttribLocation(program, 0, "aPosition");
                gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
                gl.enableVertexAttribArray(0);

                // const normals = [
                //     0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, // v0-v1-v2-v3 front
                //     1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, // v0-v3-v4-v5 right
                //     0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v0-v5-v6-v1 up
                //     -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, // v1-v6-v7-v2 left
                //     0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, // v7-v4-v3-v2 down
                //     0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0 // v4-v7-v6-v5 back  
                // ];
                const normalBuffer = gl.createBuffer();
                gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
                // gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
                // const normalData = new Uint8Array(binData, 288, 288);
                const normalData = new Float32Array(binData, 288, 288 / Float32Array.BYTES_PER_ELEMENT);
                gl.bufferData(gl.ARRAY_BUFFER, normalData, gl.STATIC_DRAW);
                gl.bindAttribLocation(program, 1, "aNormal");
                gl.vertexAttribPointer(1, 3, gl.FLOAT, false, 0, 0);
                gl.enableVertexAttribArray(1);

                // const indices = [
                //     0, 1, 2, 0, 2, 3,       // front
                //     4, 5, 6, 4, 6, 7,       // right
                //     8, 9, 10, 8, 10, 11,    // up
                //     12, 13, 14, 12, 14, 15, // left
                //     16, 17, 18, 16, 18, 19, // down
                //     20, 21, 22, 20, 22, 23  // back 
                // ];
                const indexBuffer = gl.createBuffer();
                gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
                // const indexData = new Uint8Array(binData, 576, 72);
                const indexData = new Uint16Array(binData, 576, 36);
                gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexData, gl.STATIC_DRAW);

                const projMatrix = glMatrix.mat4.create();
                glMatrix.mat4.perspective(projMatrix, 55 * Math.PI / 180, 1, 0.1, 500);
                const viewMatrix = glMatrix.mat4.create();
                glMatrix.mat4.lookAt(viewMatrix, [10, 15, 20], [0, 0, 0], [0, 1, 0]);
                const projViewMatrix = glMatrix.mat4.create();
                glMatrix.mat4.mul(projViewMatrix, projMatrix, viewMatrix);

                const modelMatrix = glMatrix.mat4.create();
                glMatrix.mat4.fromTranslation(modelMatrix, [0, 0, 0]);
                glMatrix.mat4.rotate(modelMatrix, modelMatrix, 0 * Math.PI / 180, [1, 0, 0]);
                glMatrix.mat4.scale(modelMatrix, modelMatrix, [5, 5, 5]);
                const mvpMatrix = glMatrix.mat4.create();
                glMatrix.mat4.mul(mvpMatrix, projViewMatrix, modelMatrix);
                const uMvpMatrixLocation = gl.getUniformLocation(program, "uMvpMatrix");
                gl.uniformMatrix4fv(uMvpMatrixLocation, false, mvpMatrix);
                const uModelMatrixLocation = gl.getUniformLocation(program, "uModelMatrix");
                gl.uniformMatrix4fv(uModelMatrixLocation, false, modelMatrix);

                const normalMatrix = glMatrix.mat4.create();
                glMatrix.mat4.invert(normalMatrix, modelMatrix);
                glMatrix.mat4.transpose(normalMatrix, normalMatrix);
                const uNormalMatrixLocation = gl.getUniformLocation(program, "uNormalMatrix");
                gl.uniformMatrix4fv(uNormalMatrixLocation, false, normalMatrix);

                const lightPosition = glMatrix.vec3.fromValues(7, 8, 9);
                const uLightPositionLocation = gl.getUniformLocation(program, "uLightPosition");
                gl.uniform3fv(uLightPositionLocation, lightPosition);

                gl.clearColor(0.2, 0.2, 0.2, 1);
                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
                gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
            });
        });

        function loadFile(path, callback)
        {
            const xhr = new XMLHttpRequest();
            xhr.onreadystatechange = () =>
            {
                if (xhr.readyState === 4 && xhr.status != 404)
                {
                    callback(xhr.responseText);
                }
            };
            xhr.open("GET", path, true);
            xhr.send();
        }

        function loadBin(path, callback)
        {
            const xhr = new XMLHttpRequest();

            xhr.onreadystatechange = () =>
            {
                if (xhr.readyState === 4 && xhr.status != 404)
                {
                    callback(xhr.response);
                }
            };

            xhr.open("GET", path, true);
            xhr.responseType = "arraybuffer";
            xhr.send();
        }
    </script>
</body>

</html>

BoxBlender3.gltf

{
    "asset" : {
        "generator" : "Khronos glTF Blender I/O v1.7.33",
        "version" : "2.0"
    },
    "scene" : 0,
    "scenes" : [
        {
            "name" : "Scene",
            "nodes" : [
                0
            ]
        }
    ],
    "nodes" : [
        {
            "mesh" : 0,
            "name" : "Cube"
        }
    ],
    "materials" : [
        {
            "doubleSided" : true,
            "name" : "Material",
            "pbrMetallicRoughness" : {
                "baseColorFactor" : [
                    0.800000011920929,
                    0.800000011920929,
                    0.800000011920929,
                    1
                ],
                "metallicFactor" : 0,
                "roughnessFactor" : 0.4000000059604645
            }
        }
    ],
    "meshes" : [
        {
            "name" : "Cube",
            "primitives" : [
                {
                    "attributes" : {
                        "POSITION" : 0,
                        "NORMAL" : 1
                    },
                    "indices" : 2,
                    "material" : 0
                }
            ]
        }
    ],
    "accessors" : [
        {
            "bufferView" : 0,
            "componentType" : 5126,
            "count" : 24,
            "max" : [
                1,
                1,
                1
            ],
            "min" : [
                -1,
                -1,
                -1
            ],
            "type" : "VEC3"
        },
        {
            "bufferView" : 1,
            "componentType" : 5126,
            "count" : 24,
            "type" : "VEC3"
        },
        {
            "bufferView" : 2,
            "componentType" : 5123,
            "count" : 36,
            "type" : "SCALAR"
        }
    ],
    "bufferViews" : [
        {
            "buffer" : 0,
            "byteLength" : 288,
            "byteOffset" : 0
        },
        {
            "buffer" : 0,
            "byteLength" : 288,
            "byteOffset" : 288
        },
        {
            "buffer" : 0,
            "byteLength" : 72,
            "byteOffset" : 576
        }
    ],
    "buffers" : [
        {
            "byteLength" : 648,
            "uri" : "BoxBlender3.bin"
        }
    ]
}

Loading textures and shaders using Fetch API (async/await)

Playgrounds:

image

I placed loading of texture in es6-module in separated file:

load-texture.js

import { gl } from "./webgl-context.js";

export default function loadTexture(url, minType = gl.NEAREST, magType = gl.NEAREST) {
    return new Promise(resolve => {
        const image = new Image();
        image.onload = () => {
            const texture = gl.createTexture();
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minType);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magType);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
            resolve(texture);
        };
        image.src = url;
    });
}

and call it like this:

index.js

import loadTexture from "./load-texture.js";

async function init() {
    // ...
    const texture = await loadTexture("./assets/textures/texture.png", gl.LINEAR, gl.LINEAR);
    gl.bindTexture(gl.TEXTURE_2D, texture);
    // ...
}

init();

Sprite in SFML style using pure WebGL 1.0 and JavaScript

This example loads sprites from the FreeTexturePacker sprite sheet and uses a sprite in SFML style

Playgrounds:

image