Claude Agent Skill · by Cloudai X

Threejs Loaders

Install Threejs Loaders skill for Claude Code from cloudai-x/threejs-skills.

Install
Terminal · npx
$npx skills add https://github.com/cloudai-x/threejs-skills --skill threejs-loaders
Works with Paperclip

How Threejs Loaders fits into a Paperclip company.

Threejs Loaders drops into any Paperclip agent that handles this kind of work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.

S
SaaS FactoryPaired

Pre-configured AI company — 18 agents, 18 skills, one-time purchase.

$27$59
Explore pack
Source file
SKILL.md623 lines
Expand
---name: threejs-loadersdescription: Three.js asset loading - GLTF, textures, images, models, async patterns. Use when loading 3D models, textures, HDR environments, or managing loading progress.--- # Three.js Loaders ## Quick Start ```javascriptimport * 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. ```javascriptconst 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 ```javascriptconst 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 ```javascriptconst 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. ```javascriptconst 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 ```javascriptimport { 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. ```javascriptimport { 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. ```javascriptimport { 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 ```javascriptimport { 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 ```javascriptimport { 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/three@0.160.0/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 ```javascriptloader.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 ```javascriptimport { 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 ```javascriptimport { 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 ```javascriptimport { 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 ```javascriptimport { 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 ```javascriptfunction 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 ```javascriptasync 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 ```javascriptclass 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 ```javascriptconst loader = new THREE.TextureLoader();const texture = loader.load("data:image/png;base64,iVBORw0KGgo...");``` ### Blob URL ```javascriptasync 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 textures2. **Load progressively**: Show placeholders while loading3. **Lazy load**: Only load what's needed4. **Use CDN**: Faster asset delivery5. **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