Threejs Loaders

calesthio/OpenMontage/.claude/skills/threejs-loaders

by calesthio9327439db69021ab4b0e2776729bf3b58fdb5a87No license65K starsListed Oct 9, 2026Updated Oct 9, 2026Repository updated 5 days ago

Three.js asset loading - GLTF, textures, images, models, async patterns. Use when loading 3D models, textures, HDR environments, or managing loading progress.

Instructions onlySoftware Development
AI-generated overview

Reference guide for loading 3D models, textures and HDR environments in Three.js, including async and caching patterns.

What it does
This skill is a code reference for Three.js asset loading. It shows how to load GLTF/GLB, OBJ/MTL, FBX, STL and PLY models, textures, cube maps and HDR/EXR environments, and how to coordinate them with LoadingManager. It also covers Draco and KTX2 compression, promisified and parallel loading, caching, error handling and performance tips.
When to use it
Use it when writing Three.js code that must bring external 3D models, textures or environment maps into a scene. It fits tasks involving loading progress, compressed assets, or asset caching and error handling.
Requirements
Requires a Three.js project with the addon loaders (GLTFLoader, RGBELoader, DRACOLoader, KTX2Loader and others) available. Some examples reference external decoder and transcoder files hosted on CDNs, so network access may be needed. No scripts ship with the skill; it is instructions only.

Three.js Loaders

Quick Start

javascript
import * as THREE from "three";import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
// Load GLTF modelconst loader = new GLTFLoader();loader.load("model.glb", (gltf) => {  scene.add(gltf.scene);});
// Load textureconst textureLoader = new THREE.TextureLoader();const texture = textureLoader.load("texture.jpg");

LoadingManager

Coordinate multiple loaders and track progress.

javascript
const manager = new THREE.LoadingManager();
// Callbacksmanager.onStart = (url, loaded, total) => {  console.log(`Started loading: ${url}`);};
manager.onLoad = () => {  console.log("All assets loaded!");  startGame();};
manager.onProgress = (url, loaded, total) => {  const progress = (loaded / total) * 100;  console.log(`Loading: ${progress.toFixed(1)}%`);  updateProgressBar(progress);};
manager.onError = (url) => {  console.error(`Error loading: ${url}`);};
// Use manager with loadersconst textureLoader = new THREE.TextureLoader(manager);const gltfLoader = new GLTFLoader(manager);
// Load assetstextureLoader.load("texture1.jpg");textureLoader.load("texture2.jpg");gltfLoader.load("model.glb");// onLoad fires when ALL are complete

Texture Loading

TextureLoader

javascript
const loader = new THREE.TextureLoader();
// Callback styleloader.load(  "texture.jpg",  (texture) => {    // onLoad    material.map = texture;    material.needsUpdate = true;  },  undefined, // onProgress - not supported for image loading  (error) => {    // onError    console.error("Error loading texture", error);  },);
// Synchronous (returns texture, loads async)const texture = loader.load("texture.jpg");material.map = texture;

Texture Configuration

javascript
const texture = loader.load("texture.jpg", (tex) => {  // Color space (important for color accuracy)  tex.colorSpace = THREE.SRGBColorSpace; // For color/albedo maps  // tex.colorSpace = THREE.LinearSRGBColorSpace;  // For data maps (normal, roughness)
  // Wrapping  tex.wrapS = THREE.RepeatWrapping;  tex.wrapT = THREE.RepeatWrapping;  // ClampToEdgeWrapping, RepeatWrapping, MirroredRepeatWrapping
  // Repeat/offset  tex.repeat.set(2, 2);  tex.offset.set(0.5, 0.5);  tex.rotation = Math.PI / 4;  tex.center.set(0.5, 0.5);
  // Filtering  tex.minFilter = THREE.LinearMipmapLinearFilter; // Default  tex.magFilter = THREE.LinearFilter; // Default  // NearestFilter - pixelated  // LinearFilter - smooth  // LinearMipmapLinearFilter - smooth with mipmaps
  // Anisotropic filtering (sharper at angles)  tex.anisotropy = renderer.capabilities.getMaxAnisotropy();
  // Flip Y (usually true for standard textures)  tex.flipY = true;
  tex.needsUpdate = true;});

CubeTextureLoader

For environment maps and skyboxes.

javascript
const loader = new THREE.CubeTextureLoader();
// Load 6 facesconst cubeTexture = loader.load([  "px.jpg",  "nx.jpg", // positive/negative X  "py.jpg",  "ny.jpg", // positive/negative Y  "pz.jpg",  "nz.jpg", // positive/negative Z]);
// Use as backgroundscene.background = cubeTexture;
// Use as environment mapscene.environment = cubeTexture;material.envMap = cubeTexture;

HDR/EXR Loading

javascript
import { RGBELoader } from "three/addons/loaders/RGBELoader.js";import { EXRLoader } from "three/addons/loaders/EXRLoader.js";
// HDRconst rgbeLoader = new RGBELoader();rgbeLoader.load("environment.hdr", (texture) => {  texture.mapping = THREE.EquirectangularReflectionMapping;  scene.environment = texture;  scene.background = texture;});
// EXRconst exrLoader = new EXRLoader();exrLoader.load("environment.exr", (texture) => {  texture.mapping = THREE.EquirectangularReflectionMapping;  scene.environment = texture;});

PMREMGenerator

Generate prefiltered environment maps for PBR.

javascript
import { RGBELoader } from "three/addons/loaders/RGBELoader.js";
const pmremGenerator = new THREE.PMREMGenerator(renderer);pmremGenerator.compileEquirectangularShader();
new RGBELoader().load("environment.hdr", (texture) => {  const envMap = pmremGenerator.fromEquirectangular(texture).texture;
  scene.environment = envMap;  scene.background = envMap;
  texture.dispose();  pmremGenerator.dispose();});

GLTF/GLB Loading

The most common 3D format for web.

javascript
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
const loader = new GLTFLoader();
loader.load("model.glb", (gltf) => {  // The loaded scene  const model = gltf.scene;  scene.add(model);
  // Animations  const animations = gltf.animations;  if (animations.length > 0) {    const mixer = new THREE.AnimationMixer(model);    animations.forEach((clip) => {      mixer.clipAction(clip).play();    });  }
  // Cameras (if any)  const cameras = gltf.cameras;
  // Asset info  console.log(gltf.asset); // Version, generator, etc.
  // User data from Blender/etc  console.log(gltf.userData);});

GLTF with Draco Compression

javascript
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";import { DRACOLoader } from "three/addons/loaders/DRACOLoader.js";
const dracoLoader = new DRACOLoader();dracoLoader.setDecoderPath(  "https://www.gstatic.com/draco/versioned/decoders/1.5.6/",);dracoLoader.preload();
const gltfLoader = new GLTFLoader();gltfLoader.setDRACOLoader(dracoLoader);
gltfLoader.load("compressed-model.glb", (gltf) => {  scene.add(gltf.scene);});

GLTF with KTX2 Textures

javascript
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";import { KTX2Loader } from "three/addons/loaders/KTX2Loader.js";
const ktx2Loader = new KTX2Loader();ktx2Loader.setTranscoderPath(  "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/libs/basis/",);ktx2Loader.detectSupport(renderer);
const gltfLoader = new GLTFLoader();gltfLoader.setKTX2Loader(ktx2Loader);
gltfLoader.load("model-with-ktx2.glb", (gltf) => {  scene.add(gltf.scene);});

Process GLTF Content

javascript
loader.load("model.glb", (gltf) => {  const model = gltf.scene;
  // Enable shadows  model.traverse((child) => {    if (child.isMesh) {      child.castShadow = true;      child.receiveShadow = true;    }  });
  // Find specific mesh  const head = model.getObjectByName("Head");
  // Adjust materials  model.traverse((child) => {    if (child.isMesh && child.material) {      child.material.envMapIntensity = 0.5;    }  });
  // Center and scale  const box = new THREE.Box3().setFromObject(model);  const center = box.getCenter(new THREE.Vector3());  const size = box.getSize(new THREE.Vector3());
  model.position.sub(center);  const maxDim = Math.max(size.x, size.y, size.z);  model.scale.setScalar(1 / maxDim);
  scene.add(model);});

Other Model Formats

OBJ + MTL

javascript
import { OBJLoader } from "three/addons/loaders/OBJLoader.js";import { MTLLoader } from "three/addons/loaders/MTLLoader.js";
const mtlLoader = new MTLLoader();mtlLoader.load("model.mtl", (materials) => {  materials.preload();
  const objLoader = new OBJLoader();  objLoader.setMaterials(materials);  objLoader.load("model.obj", (object) => {    scene.add(object);  });});

FBX

javascript
import { FBXLoader } from "three/addons/loaders/FBXLoader.js";
const loader = new FBXLoader();loader.load("model.fbx", (object) => {  // FBX often has large scale  object.scale.setScalar(0.01);
  // Animations  const mixer = new THREE.AnimationMixer(object);  object.animations.forEach((clip) => {    mixer.clipAction(clip).play();  });
  scene.add(object);});

STL

javascript
import { STLLoader } from "three/addons/loaders/STLLoader.js";
const loader = new STLLoader();loader.load("model.stl", (geometry) => {  const material = new THREE.MeshStandardMaterial({ color: 0x888888 });  const mesh = new THREE.Mesh(geometry, material);  scene.add(mesh);});

PLY

javascript
import { PLYLoader } from "three/addons/loaders/PLYLoader.js";
const loader = new PLYLoader();loader.load("model.ply", (geometry) => {  geometry.computeVertexNormals();  const material = new THREE.MeshStandardMaterial({ vertexColors: true });  const mesh = new THREE.Mesh(geometry, material);  scene.add(mesh);});

Async/Promise Loading

Promisified Loader

javascript
function loadModel(url) {  return new Promise((resolve, reject) => {    loader.load(url, resolve, undefined, reject);  });}
// Usageasync function init() {  try {    const gltf = await loadModel("model.glb");    scene.add(gltf.scene);  } catch (error) {    console.error("Failed to load model:", error);  }}

Load Multiple Assets

javascript
async function loadAssets() {  const [modelGltf, envTexture, colorTexture] = await Promise.all([    loadGLTF("model.glb"),    loadRGBE("environment.hdr"),    loadTexture("color.jpg"),  ]);
  scene.add(modelGltf.scene);  scene.environment = envTexture;  material.map = colorTexture;}
// Helper functionsfunction loadGLTF(url) {  return new Promise((resolve, reject) => {    new GLTFLoader().load(url, resolve, undefined, reject);  });}
function loadRGBE(url) {  return new Promise((resolve, reject) => {    new RGBELoader().load(      url,      (texture) => {        texture.mapping = THREE.EquirectangularReflectionMapping;        resolve(texture);      },      undefined,      reject,    );  });}
function loadTexture(url) {  return new Promise((resolve, reject) => {    new THREE.TextureLoader().load(url, resolve, undefined, reject);  });}

Caching

Built-in Cache

javascript
// Enable cacheTHREE.Cache.enabled = true;
// Clear cacheTHREE.Cache.clear();
// Manual cache managementTHREE.Cache.add("key", data);THREE.Cache.get("key");THREE.Cache.remove("key");

Custom Asset Manager

javascript
class AssetManager {  constructor() {    this.textures = new Map();    this.models = new Map();    this.gltfLoader = new GLTFLoader();    this.textureLoader = new THREE.TextureLoader();  }
  async loadTexture(key, url) {    if (this.textures.has(key)) {      return this.textures.get(key);    }
    const texture = await new Promise((resolve, reject) => {      this.textureLoader.load(url, resolve, undefined, reject);    });
    this.textures.set(key, texture);    return texture;  }
  async loadModel(key, url) {    if (this.models.has(key)) {      return this.models.get(key).clone();    }
    const gltf = await new Promise((resolve, reject) => {      this.gltfLoader.load(url, resolve, undefined, reject);    });
    this.models.set(key, gltf.scene);    return gltf.scene.clone();  }
  dispose() {    this.textures.forEach((t) => t.dispose());    this.textures.clear();    this.models.clear();  }}
// Usageconst assets = new AssetManager();const texture = await assets.loadTexture("brick", "brick.jpg");const model = await assets.loadModel("tree", "tree.glb");

Loading from Different Sources

Data URL / Base64

javascript
const loader = new THREE.TextureLoader();const texture = loader.load("data:image/png;base64,iVBORw0KGgo...");

Blob URL

javascript
async function loadFromBlob(blob) {  const url = URL.createObjectURL(blob);  const texture = await loadTexture(url);  URL.revokeObjectURL(url);  return texture;}

ArrayBuffer

javascript
// From fetchconst response = await fetch("model.glb");const buffer = await response.arrayBuffer();
// Parse with loaderconst loader = new GLTFLoader();loader.parse(buffer, "", (gltf) => {  scene.add(gltf.scene);});

Custom Path/URL

javascript
// Set base pathloader.setPath("assets/models/");loader.load("model.glb"); // Loads from assets/models/model.glb
// Set resource path (for textures referenced in model)loader.setResourcePath("assets/textures/");
// Custom URL modifiermanager.setURLModifier((url) => {  return `https://cdn.example.com/${url}`;});

Error Handling

javascript
// Graceful fallbackasync function loadWithFallback(primaryUrl, fallbackUrl) {  try {    return await loadModel(primaryUrl);  } catch (error) {    console.warn(`Primary failed, trying fallback: ${error}`);    return await loadModel(fallbackUrl);  }}
// Retry logicasync function loadWithRetry(url, maxRetries = 3) {  for (let i = 0; i < maxRetries; i++) {    try {      return await loadModel(url);    } catch (error) {      if (i === maxRetries - 1) throw error;      await new Promise((r) => setTimeout(r, 1000 * (i + 1)));    }  }}
// Timeoutasync function loadWithTimeout(url, timeout = 30000) {  const controller = new AbortController();  const timeoutId = setTimeout(() => controller.abort(), timeout);
  try {    const response = await fetch(url, { signal: controller.signal });    clearTimeout(timeoutId);    return response;  } catch (error) {    if (error.name === "AbortError") {      throw new Error("Loading timed out");    }    throw error;  }}

Performance Tips

  1. Use compressed formats: DRACO for geometry, KTX2/Basis for textures
  2. Load progressively: Show placeholders while loading
  3. Lazy load: Only load what's needed
  4. Use CDN: Faster asset delivery
  5. Enable cache: THREE.Cache.enabled = true
javascript
// Progressive loading with placeholderconst placeholder = new THREE.Mesh(  new THREE.BoxGeometry(1, 1, 1),  new THREE.MeshBasicMaterial({ wireframe: true }),);scene.add(placeholder);
loadModel("model.glb").then((gltf) => {  scene.remove(placeholder);  scene.add(gltf.scene);});

See Also

  • threejs-textures - Texture configuration
  • threejs-animation - Playing loaded animations
  • threejs-materials - Material from loaded models

Source and attribution

Source:calesthio/OpenMontagein.claude/skills/threejs-loadersat commit9327439

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal