Course outline
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.
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.
GLTFExporterwrites glTF. - extras
- glTF's place for your own data on any object; three.js puts
userDatathere 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
Export as text
In the first example change
{ binary: true }to{ binary: false }and the next line toconst back = await new GLTFLoader().parseAsync(JSON.stringify(glb), "");. The first line now printsexported: undefined bytes, because a .gltf result is an object, not bytes, yet the bike still loads back with all five meshes. - 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 reportsimage/png of 240 KiB, down from 1057. Smaller textures are one fix; Topic 10.5's WebP compression is a better one.
Code & diagrams
The viewport shows the copy that came back from the bytes, not the original.
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: trueConsole only. The warning line is printed by GLTFExporter itself.
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_punctualBreak 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).
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.
- ▸
GLTFExporteroptions worth knowing:onlyVisible(default true),binary,maxTextureSize,animations(pass the clips: they are not found automatically),includeCustomExtensionsandtrs(write position/rotation/scale instead of a matrix). - ▸
The exporter writes
EXT_mesh_gpu_instancingforInstancedMeshand 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 theInstancedMesh.
Remember this
- 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
How to export.
await new GLTFExporter().parseAsync(object, { binary: true })returns anArrayBuffer(a .glb); withbinary: falseit returns the JSON object of a .gltf.parseAsyncwaits 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 withGLTFLoader.parseAsync: all five names and positions return. - 3
What survives. Names, the scene tree, positions, rotations, scales,
MeshStandardMaterialandMeshPhysicalMaterial(with extensions such as clearcoat and transmission),MeshBasicMaterial(as KHR_materials_unlit), textures, animations, cameras, lights (as KHR_lights_punctual) anduserData, which becomes the node'sextras. Shared materials stay shared: the loaded front and rear wheels still use one material. - 4
What does not.
ShaderMaterialis not exported: the exporter printsGLTFExporter: 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
Getting the file onto your computer. In your own project, wrap the bytes in a
Bloband 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
What happens to a mesh with a ShaderMaterial when you export it to glTF, and why?
Why did exporting the bike make its file bigger than the textures it was built from?
Practice
Write a downloadGlb(object, name) function for your project that exports and downloads a .glb.
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