Command Palette

Search for a command to run...

Hectal
Course outline
Topic 10.1Intermediate ~18 min · step 1 of 7

Inside glTF

In one line

glTF is the standard file format for 3D on the web: a JSON description of the scene plus the raw bytes of its geometry and images. A .glb puts both in one binary file: a 12-byte header and two chunks.

0/7 · 0%

Think of it like this

Flat-pack furniture comes as one box with two things inside: an instruction sheet that says which panel goes where, and the panels and screws themselves. A .glb is that box. The JSON is the instruction sheet; the binary chunk holds the panels: numbers ready to be copied straight onto the GPU.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

glTF
"GL Transmission Format": the standard file format for delivering 3D scenes, made by the Khronos Group (the people behind WebGL).
.glb
The binary form of glTF: one file holding the JSON description and the raw data together.
accessor
An entry in the JSON that says how to read a run of numbers: how many, which type (for example three floats per vertex), and where they are.
buffer
A block of raw bytes: vertex positions, normals, indices, images. A .glb has one, in its BIN chunk.
chunk
One section of a .glb: its length, its type (JSON or BIN) and its data.
base64
A way to write bytes as plain text (letters, digits, + and /). The text is a third longer than the bytes it stands for.
little-endian
Storing a number with its smallest byte first. glTF numbers are little-endian; a DataView needs true to read them that way.

Step by step

01Open the file in the inspector

The widget loads /data/3d/models/lumen-bike.glb, the course's own bike exported from code, and lists what is inside: counts, meshes, extensions and the GPU cost of its buffers and textures. Switch to "The JSON chunk" to read the description itself.

02Read the header yourself

The first live example fetches the same file and reads it with a DataView: getUint32(0, true) reads 4 bytes as a number, little-endian (the smallest byte first, as glTF requires). It prints glTF, version 2, and the total length, 1,325,548 bytes: the size of the whole file.

03Walk the chunks

After the header it loops over chunks: read the length, read the type, skip the data. The JSON chunk is 28,296 bytes; the BIN chunk is 1,297,224. 12 + 8 + 28,296 + 8 + 1,297,224 = 1,325,548: every byte is accounted for.

04Compare .gltf and .glb

The second example exports one tube both ways. The .gltf is 6,060 characters of JSON with its buffer as a data: URL; the .glb is 4,848 bytes. The difference is the base64 text and the JSON's own spacing. Both keep the name down-tube and the material.

Try it yourself

  1. 1

    Count every byte

    In "Read a .glb file by hand", add console.log("sum:", 12 + 8 + 28296 + 8 + 1297224); at the end of the async function. It prints sum: 1325548, the total length from the header: a .glb has nothing else in it.

  2. 2

    Read the Fox instead

    Change the URL to /data/3d/models/Fox.glb. It prints nodes 26 | meshes 1 | accessors 71 | materials 1 | images 1: one mesh with a skeleton of nodes (the bones). Its generator line is missing (undefined), because the file has none: generator is optional.

Code & diagrams

Try it: interactive

Inside the Lumen bike's file

Read the counts, then open "The JSON chunk": find the node called down-tube and follow its mesh number to the meshes list.

Read a .glb file by hand New tab

No three.js at all: just fetch and a DataView. This is what GLTFLoader does first.

Console

(nothing was printed)

Expected console output

magic: glTF | version: 2 | total length: 1325548 bytes
chunk JSON 28296 bytes
  nodes 33 | meshes 25 | accessors 106 | materials 5 | images 1
  generator: THREE.GLTFExporter r186
chunk BIN 1297224 bytes
.gltf or .glb: the same tube, two files New tab

The colour comes back as 1.000, 0.147, 0.025: glTF stores colours in linear space, and that is #ff6b2c converted (Topic 5.6).

idle

Console

Run the scene to see its console.

Expected console output

.gltf: 6060 characters of JSON
  buffer starts: data:application/octet-stream;base64,...
.glb:  4848 bytes
names kept: down-tube | material colour: 1.000, 0.147, 0.025, 1.000

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Read the length as big-endian

In the first example change view.getUint32(8, true) to view.getUint32(8) (no second argument).

terminal
$ Run
── what you'll see ──
magic: glTF | version: 2 | total length: 3963163648 bytes
Only this number is wrong: the version and the chunk lengths are still read with `true`.

Myth vs fact

Myth

A .gltf and a .glb are different formats.

Fact

They are the same format in two packagings. Tools convert between them without changing the scene: npx @gltf-transform/cli copy in.gltf out.glb.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Accessors can be sparse (a few changed values on top of a base) and can be normalised integers read as floats; KHR_mesh_quantization stores positions as 16-bit integers, which are smaller than floats and which the GPU reads directly: that kind of shrinking needs no separate decoder.

  • ▸

    The JSON's extensionsUsed lists optional features; extensionsRequired lists features a loader must support or refuse the file. The exported bike requires EXT_mesh_gpu_instancing, because Phase 7's spokes are an InstancedMesh: a loader without it cannot draw them.

Remember this

  1. 1

    The problem this solves: before glTF, every 3D tool had its own format, and every app needed its own importer for each one. glTF (the Khronos Group's "GL Transmission Format", version 2.0, also an ISO standard) is made for delivery: its arrays are laid out the way GPUs read them, so loading is mostly copying bytes, not converting them. It is often called the JPEG of 3D.

  2. 2

    Two flavours. A .gltf file is JSON; its binary data is either in separate .bin and image files next to it, or embedded as base64 text (a third bigger). A .glb is one binary file holding the same JSON plus the bytes. Web apps almost always ship .glb: one request, no base64 overhead.

  3. 3

    The JSON is a chain of lists. scenes list root nodes; a node has a name, a position, rotation and scale, children, and maybe a mesh; a mesh has primitives (one per material) that point to accessors; an accessor says "this many numbers, of this type, starting here" in a bufferView, which is a slice of a buffer. materials, textures, images and animations work the same way: everything refers to everything else by number.

  4. 4

    A .glb, byte by byte. The first 12 bytes are the header: the letters glTF, the version (2) and the total length. Then each chunk is its own length (4 bytes), its type (JSON or BIN) and its data. The first live example reads the course's own bike file this way, with nothing but fetch and a DataView.

  5. 5

    What the numbers tell you. The exported bike has 33 nodes, 25 meshes and 106 accessors, but a 1.30 MB binary chunk: almost all of it is one tyre texture, stored as PNG. The JSON is 28.3 KB. Reading these numbers is the first step of every "why is this model so slow to load" investigation.

Explain it without notes

01

Explain the difference between a node, a mesh and an accessor in glTF.

02

Why do web apps ship .glb rather than .gltf with embedded data?

Practice

01

Extend the first example to print the name of every node that has a mesh.

02

Using the inspector, find how many bytes of the bike's file are the tyre texture.

Done when you can

  • Say what the header, the JSON chunk and the BIN chunk of a .glb hold

  • Follow a node to its mesh, accessor and buffer view

  • Read a .glb's header with a DataView

  • Choose .glb over .gltf for delivery, and say why