Three.js Exporting — Saving Scenes to glTF, OBJ, and STL
In this tutorial, you will learn about Three.js Exporting. We cover key concepts, practical examples, and best practices to help you master this topic.
Three.js exporting converts in-memory 3D scenes to file formats like glTF, OBJ, and STL using dedicated exporter classes for sharing and 3D printing.
What You'll Learn
By the end of this guide, you will export Three.js scenes to glTF binary and text formats, export individual meshes to OBJ and STL, handle embedded textures, configure export options, and trigger file downloads from the browser.
Why Exporting Matters
Durga Antivirus Pro's threat visualization dashboard lets analysts export network topology as glTF files for offline review in 3D modeling tools. Exporting bridges the gap between runtime visualization and permanent asset storage.
flowchart LR
A[Three.js Scene] --> B[GLTFExporter]
A --> C[OBJExporter]
A --> D[STLExporter]
B --> E[glTF Binary (.glb)]
B --> F[glTF Text (.gltf + .bin)]
C --> G[OBJ + MTL]
D --> H[STL (ASCII/Binary)]
E --> I[Download or Save]
Exporting to glTF Binary
import { GLTFExporter } from 'three/addons/exporters/GLTFExporter.js';
var exporter = new GLTFExporter();
exporter.parse(
scene,
function(glb) {
var blob = new Blob([glb], { type: 'application/octet-stream' });
var url = URL.createObjectURL(blob);
var link = document.createElement('a');
link.href = url;
link.download = 'scene.glb';
link.click();
},
function(error) {
console.error('Export error:', error);
},
{ binary: true }
);
Expected output: A download prompt for a .glb file containing the entire scene.
Why binary: Binary glTF (GLB) is a single file with all data embedded. It is smaller than text glTF and loads faster.
Exporting to glTF Text
exporter.parse(
scene,
function(result) {
var output = JSON.stringify(result, null, 2);
var blob = new Blob([output], { type: 'application/json' });
var url = URL.createObjectURL(blob);
var link = document.createElement('a');
link.href = url;
link.download = 'scene.gltf';
link.click();
// Also need to save .bin file with buffer data
if (result.buffers) {
result.buffers.forEach(function(buffer, i) {
var binBlob = new Blob([buffer]);
var binUrl = URL.createObjectURL(binBlob);
var binLink = document.createElement('a');
binLink.href = binUrl;
binLink.download = 'scene' + i + '.bin';
binLink.click();
});
}
},
undefined,
{ binary: false }
);
Expected output: A .gltf JSON file and separate .bin buffer files.
Exporting to STL (3D Printing)
import { STLExporter } from 'three/addons/exporters/STLExporter.js';
var stlExporter = new STLExporter();
var stlData = stlExporter.parse(mesh, { binary: false });
var blob = new Blob([stlData], { type: 'text/plain' });
var url = URL.createObjectURL(blob);
var link = document.createElement('a');
link.href = url;
link.download = 'model.stl';
link.click();
Expected output: A downloadable .stl file with the mesh geometry, suitable for 3D printing or CAD import.
Export Options Reference
exporter.parse(scene, onSuccess, onError, {
binary: true, // true = .glb, false = .gltf + .bin
trs: false, // Use TRS or matrix
onlyVisible: true, // Skip invisible objects
truncateDrawRange: true, // Optimize geometry buffers
embedImages: true, // Embed textures in the file
animations: [], // Include specific animations
includeCustomExtensions: false
});
Common Mistakes
1. Exporting With Missing Normals
Some formats require normals. Run geometry.computeVertexNormals() before exporting.
2. Binary Data Not Converted to Blob
The exporter callback provides ArrayBuffer data. Wrapping it in a Blob with the correct MIME type is required for download.
3. Exporting Objects With Undisposed Resources
Exporting a scene that has materials with disposed textures results in missing data. Export before cleanup.
4. Not Handling Export Errors
Always provide an error callback. Large scenes may fail due to memory limits.
5. Missing UV Coordinates
glTF requires UV coordinates for textured materials. Add UVs if your geometry lacks them.
Practice Questions
Q1: What is the difference between .glb and .gltf? A: .glb is a single binary file with all data embedded. .gltf is a JSON file referencing separate .bin and texture files.
Q2: Which export format is best for 3D printing? A: STL (stereolithography). It stores raw triangle geometry without materials or animations.
Q3: How do you trigger a file download from an export? A: Create a Blob from the export data, generate a URL with URL.createObjectURL, create an anchor element, and call .click().
Q4: What export option embeds images in the glTF file?
A: embedImages: true converts textures to embedded base64 data within the glTF JSON or GLB binary.
Q5: How do you export only visible objects?
A: Set onlyVisible: true in the exporter options. Invisible objects are excluded from the output.
Challenge: Build a scene editor that lets users add, rotate, and scale objects, then export the entire scene as a .glb file with textures embedded.
FAQ
Try It Yourself — 3D Scene Exporter
Build a page with a simple scene and export buttons for glTF, OBJ, and STL formats.
<!DOCTYPE html>
<html>
<head>
<title>Three.js Scene Exporter</title>
<style>
body { margin: 0; overflow: hidden; font-family: sans-serif; }
#ui { position: absolute; top: 10px; left: 10px; background: rgba(0,0,0,0.8); color: white; padding: 15px; border-radius: 8px; }
#ui button { padding: 6px 12px; margin: 4px; cursor: pointer; }
#status { margin-top: 8px; font-size: 12px; color: #aaa; }
</style>
</head>
<body>
<div id="ui">
<button onclick="exportGLB()">Export GLB</button>
<button onclick="exportSTL()">Export STL</button>
<div id="status">Ready</div>
</div>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.170.0/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { GLTFExporter } from "three/addons/exporters/GLTFExporter.js";
import { STLExporter } from "three/addons/exporters/STLExporter.js";
var scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a2e);
var camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 100);
camera.position.set(5, 4, 8);
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
renderer.setPixelRatio(devicePixelRatio);
document.body.appendChild(renderer.domElement);
var controls = new OrbitControls(camera, renderer.domElement);
var ambient = new THREE.AmbientLight(0x404060);
scene.add(ambient);
var dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(5, 10, 7);
scene.add(dirLight);
var box = new THREE.Mesh(
new THREE.BoxGeometry(1.5, 1.5, 1.5),
new THREE.MeshStandardMaterial({ color: 0xff6b35, roughness: 0.3, metalness: 0.1 })
);
box.position.x = -2;
scene.add(box);
var sphere = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 32),
new THREE.MeshStandardMaterial({ color: 0x4ecdc4, roughness: 0.1, metalness: 0.8 })
);
sphere.position.x = 2;
scene.add(sphere);
var ground = new THREE.Mesh(
new THREE.PlaneGeometry(10, 10),
new THREE.MeshStandardMaterial({ color: 0x333355, side: THREE.DoubleSide })
);
ground.rotation.x = -Math.PI / 2;
ground.position.y = -1.5;
scene.add(ground);
var statusDiv = document.getElementById('status');
window.exportGLB = function() {
var exporter = new GLTFExporter();
exporter.parse(scene, function(result) {
var blob = new Blob([result], { type: 'application/octet-stream' });
var url = URL.createObjectURL(blob);
var link = document.createElement('a');
link.href = url;
link.download = 'scene.glb';
link.click();
statusDiv.textContent = 'GLB exported successfully';
}, function(err) {
statusDiv.textContent = 'Export error: ' + err.message;
}, { binary: true });
};
window.exportSTL = function() {
var exporter = new STLExporter();
var objects = [];
scene.traverse(function(child) {
if (child.isMesh) objects.push(child);
});
var stlData = exporter.parse(scene, { binary: false });
var blob = new Blob([stlData], { type: 'text/plain' });
var url = URL.createObjectURL(blob);
var link = document.createElement('a');
link.href = url;
link.download = 'scene.stl';
link.click();
statusDiv.textContent = 'STL exported successfully';
};
function animate() {
box.rotation.y += 0.01;
sphere.rotation.x += 0.005;
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
window.addEventListener("resize", function() {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
</script>
</body>
</html>
What's Next
Learn about the Loading Manager for tracking asset loading progress.
Loading Manager — Progress tracking for asset loading. Sprites — Billboard sprites in Three.js.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro