Command Palette

Search for a command to run...

Hectal
Course outline
Topic 10.2Intermediate ~19 min · step 2 of 7

Export Our Bike as glTF

In one line

GLTFExporter turns any three.js scene into a .glb or .gltf: names, positions, standard materials, textures, lights and userData survive. Custom shaders do not, and textures are re-encoded, which can make the file bigger.

0/7 · 0%

Think of it like this

Saving a document as PDF so someone without your program can open it. Most of it survives, but anything your program does specially (a macro, a font it made up) may not. Exporting a three.js scene to glTF works the same way.

Words you'll meet

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

exporter
Code that writes a scene out to a file format. GLTFExporter writes glTF.
extras
glTF's place for your own data on any object; three.js puts userData there and reads it back.
re-encode
Decoding an image and saving it again in another format, here PNG, which is lossless and often much bigger than a JPEG.
model/gltf-binary
The official MIME type of a .glb file (a .gltf is model/gltf+json).

Step by step

01Build, export, load back

The first example builds a small bike from tubes and tori (as in Phase 4), exports it to bytes, and parses those bytes with GLTFLoader.parseAsync(buffer, ""). The console lists the meshes that came back and checks a position and the shared material.

02Look at what changed

The second example exports a textured tyre with userData, a mesh with a custom shader and a point light, as a .gltf so the JSON can be read. It prints the image's type and size, the extras, whether the shader mesh has a material, and the extensions used.

03Read the warning

The custom shader causes the only warning: GLTFExporter: THREE.ShaderMaterial not supported. The export still succeeds. A warning like this can hide in a busy console, so check exported files with an inspector before you hand them over.

04Save it in your project

In lumen-3d, run the export in the browser console of your running showroom (or a button you add for yourself), download lumen-bike.glb and put it in public/data/3d/models/. This site's copy was made exactly that way from the course's bike after Phase 9 (with the handlebar and the crankset).

Try it yourself

  1. 1

    Export as text

    In the first example change { binary: true } to { binary: false } and the next line to const back = await new GLTFLoader().parseAsync(JSON.stringify(glb), "");. The first line now prints exported: undefined bytes, because a .gltf result is an object, not bytes, yet the bike still loads back with all five meshes.

  2. 2

    Keep the JPEG small

    In the second example add { binary: false, maxTextureSize: 512 } as the options. The PNG now has a quarter of the pixels (512 × 512 instead of 1024 × 1024) and the line reports image/png of 240 KiB, down from 1057. Smaller textures are one fix; Topic 10.5's WebP compression is a better one.

Code & diagrams

Export the bike and load it back New tab

The viewport shows the copy that came back from the bytes, not the original.

idle

Console

Run the scene to see its console.

Expected console output

exported: 71000 bytes
loaded back: 5 meshes: down-tube, top-tube, seat-tube, rear-wheel, front-wheel
front-wheel position: 0.58, 0.35, 0.00 | materials shared: true
What an export keeps, changes and drops New tab

Console only. The warning line is printed by GLTFExporter itself.

idle

Console

Run the scene to see its console.

Expected console output

GLTFExporter: THREE.ShaderMaterial not supported.
texture: was a 343 KiB JPEG, now image/png of 1057 KiB
userData kept as extras: {"partNumber":"LB-01-06","price":89}
custom shader node has a material: false
light kept with: KHR_lights_punctual

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

Export before the texture has loaded

In the second example change await new THREE.TextureLoader().loadAsync(...) to new THREE.TextureLoader().load(...) (no await).

terminal
$ Run
── what you'll see ──
Uncaught Error: THREE.GLTFExporter: No valid image data found. Unable to process texture.
The whole export fails: nothing is written.

Myth vs fact

Myth

Exporting and re-importing gives you exactly the same scene.

Fact

Standard parts survive, but custom shaders are dropped, Phong and Lambert are approximated, and textures are re-encoded. Always inspect what came out.

Pro corner

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

  • ▸

    GLTFExporter options worth knowing: onlyVisible (default true), binary, maxTextureSize, animations (pass the clips: they are not found automatically), includeCustomExtensions and trs (write position/rotation/scale instead of a matrix).

  • ▸

    The exporter writes EXT_mesh_gpu_instancing for InstancedMesh and marks it required. Tools or engines without that extension refuse the file; if a partner needs plain meshes, export a copy built with ordinary meshes instead of the InstancedMesh.

Remember this

  1. 1

    The problem this solves: the bike lives in code, but Ama's designers work in Blender, and the next version of the site should load one file instead of running 200 lines of geometry code. Exporting writes the scene in the shared format both sides understand.

  2. 2

    How to export. await new GLTFExporter().parseAsync(object, { binary: true }) returns an ArrayBuffer (a .glb); with binary: false it returns the JSON object of a .gltf. parseAsync waits for textures to be read, so call it after they have loaded. The first example exports a five-part bike to 71,000 bytes and loads it straight back with GLTFLoader.parseAsync: all five names and positions return.

  3. 3

    What survives. Names, the scene tree, positions, rotations, scales, MeshStandardMaterial and MeshPhysicalMaterial (with extensions such as clearcoat and transmission), MeshBasicMaterial (as KHR_materials_unlit), textures, animations, cameras, lights (as KHR_lights_punctual) and userData, which becomes the node's extras. Shared materials stay shared: the loaded front and rear wheels still use one material.

  4. 4

    What does not. ShaderMaterial is not exported: the exporter prints GLTFExporter: THREE.ShaderMaterial not supported. and the mesh has no material in the file. Phong and Lambert materials are written in the standard model with a warning (GLTFExporter: Use MeshStandardMaterial or MeshBasicMaterial for best results.), so they look different when loaded. Textures are re-encoded: the 343 KiB JPEG tyre tread becomes a 1,057 KiB PNG inside the file, which is why the bike's export is about 1.3 MB. Topic 10.5 shrinks it again.

  5. 5

    Getting the file onto your computer. In your own project, wrap the bytes in a Blob and click a temporary link: const a = document.createElement("a"); a.href = URL.createObjectURL(new Blob([glb], { type: "model/gltf-binary" })); a.download = "lumen-bike.glb"; a.click();. The preview on this page is sandboxed and cannot download files, so the examples only count the bytes.

Explain it without notes

01

What happens to a mesh with a ShaderMaterial when you export it to glTF, and why?

02

Why did exporting the bike make its file bigger than the textures it was built from?

Practice

01

Write a downloadGlb(object, name) function for your project that exports and downloads a .glb.

02

Export the tyre from the second example with a price in userData, load it back, and print the price from the loaded copy.

Done when you can

  • Export a scene to .glb and to .gltf with GLTFExporter

  • Say which materials, objects and data survive an export

  • Spot the ShaderMaterial warning and the texture re-encoding

  • Download an export from your own project