Image
ProjectsAboutResumeBlogDesign
Contact
Image
Brandon

Full stack EngineerFull stack Engineer

ProjectsAboutResumeDesignBlog3D StuffContact
Image

Blog

0 min read

GLB Compression for Three.js Projects

Brandon Nolan

Brandon Nolan

September 1, 2025

Compressing GLB files is crucial for optimizing 3D assets in web-based Three.js projects. Large models can slow down load times and impact performance, especially on mobile devices. In this post, we'll explore how to compress GLB files using the gltf-transform library with Draco compression for vanilla Three.js projects. We'll also cover an alternative approach for React Three Fiber users using the gltfjsx tool with its --transform flag, which automates compression and generates reusable JSX components.

The goal is to help you reduce file sizes by up to 70%-90% while maintaining visual quality, making your 3D scenes faster and more efficient. Let's dive in!

Why Compress GLB Files?

GLB files (the binary format of GLTF) are widely used in Three.js for 3D models. However, they can be large due to complex geometries, high-resolution textures, and redundant nodes. Compression addresses these issues by:

  • Reducing geometry size with Draco compression.
  • Optimizing textures by resizing and converting to efficient formats like WebP.
  • Pruning unnecessary nodes to improve rendering performance.
  • Deduplicating resources to eliminate redundant data.

This post provides two approaches: a Node.js script for vanilla Three.js users and the gltfjsx tool for React Three Fiber users. You can skip ahead to the section that suits your needs:

  • Vanilla Three.js: GLB Compression Script
  • React Three Fiber: Using gltfjsx --transform

Vanilla Three.js: GLB Compression Script

For vanilla Three.js projects, you can use a Node.js script leveraging the gltf-transform library to compress GLB files. This script processes all .glb files in a specified folder, applying Draco compression, texture optimization, and pruning.

Setup

  1. Create a project directory and initialize a Node.js project:
    npm init -y
  2. Install dependencies:
    npm install @gltf-transform/core @gltf-transform/extensions @gltf-transform/functions draco3dgltf sharp
  3. Create a glb-files folder in your project directory and place your .glb files there.
  4. Add the compression script (see implementation below).

Implementation

Here's the script to compress GLB files:

import fs from "fs";
import path from "path";
import sharp from "sharp";
import draco3d from "draco3dgltf";

import { NodeIO } from "@gltf-transform/core";
import { ALL_EXTENSIONS } from "@gltf-transform/extensions";
import {
  dedup,
  draco,
  prune,
  resample,
  textureCompress,
} from "@gltf-transform/functions";

// === CONFIGURATION ===
const FOLDER_PATH = "./glb-files"; // folder with your .glb files
const OUTPUT_SUFFIX = ".compressed.glb";

async function compressGLB(filePath) {
  const encoder = await draco3d.createEncoderModule();
  const decoder = await draco3d.createDecoderModule();

  const io = new NodeIO()
    .registerExtensions(ALL_EXTENSIONS)
    .registerDependencies({
      "draco3d.encoder": encoder,
      "draco3d.decoder": decoder,
    });

  console.log(`Processing: ${filePath}`);

  const document = await io.read(filePath);

  await document.transform(
    resample(), // Optimizes animations
    prune(), // Removes unused nodes
    dedup(), // Removes duplicate resources
    draco(), // Applies Draco compression
    textureCompress({
      encoder: sharp,
      targetFormat: "webp",
      resize: [1024, 1024], // Resize textures to 1024x1024
    })
  );

  const parsed = path.parse(filePath);
  const outputPath = path.join(parsed.dir, `${parsed.name}${OUTPUT_SUFFIX}`);
  await io.write(outputPath, document);

  console.log(`Saved: ${outputPath}`);
}

async function run() {
  const files = fs.readdirSync(FOLDER_PATH);

  const glbFiles = files.filter((file) => file.toLowerCase().endsWith(".glb"));

  for (const file of glbFiles) {
    const fullPath = path.join(FOLDER_PATH, file);
    await compressGLB(fullPath);
  }

  console.log("Compression complete.");
}

run().catch((err) => console.error(err));

How It Works

  • Line 1-7: Imports - The script imports Node.js modules (fs, path), sharp for texture processing, draco3dgltf for Draco compression, and gltf-transform modules for GLTF manipulation.
  • Line 12-13: Configuration - Defines the input folder (glb-files) and output suffix (.compressed.glb).
  • Line 15-35: compressGLB Function - Processes a single GLB file:
    • Initializes Draco encoder/decoder for geometry compression.
    • Uses NodeIO to read the GLB file and apply transformations:
      • resample(): Optimizes animations by reducing keyframes.
      • prune(): Removes unused nodes and groups.
      • dedup(): Eliminates duplicate resources (e.g., identical textures).
      • draco(): Applies Draco compression to geometry.
      • textureCompress(): Converts textures to WebP and resizes to 1024x1024.
    • Saves the compressed file with the .compressed.glb suffix.
  • Line 37-47: run Function - Scans the glb-files folder, filters for .glb files, and processes each one.

Usage

  1. Place your .glb files in the glb-files folder.
  2. Run the script:
    node index.js
  3. Find compressed files in the glb-files folder (e.g., model.compressed.glb).
  4. Use the compressed .glb files in your Three.js project by loading them with GLTFLoader.

Package.json

{
  "name": "glb-compressor",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "@gltf-transform/core": "^3.10.0",
    "@gltf-transform/extensions": "^3.10.0",
    "@gltf-transform/functions": "^3.10.0",
    "draco3dgltf": "^1.5.7",
    "sharp": "^0.33.2"
  }
}

Notes

  • Ensure Node.js is installed.
  • The compressed .glb files are optimized for web use and can be loaded in Three.js using GLTFLoader.
  • For Draco-compressed models, include the Draco decoder in your Three.js project (e.g., via CDN: https://www.gstatic.com/draco/v1/decoders/).

React Three Fiber: Using gltfjsx --transform

For React Three Fiber users, the gltfjsx tool provides a powerful way to compress GLB files and generate reusable JSX components simultaneously. The --transform flag automates compression tasks similar to the script above.

Why Use gltfjsx?

The default GLTF workflow in Three.js has limitations:

  • Models are loaded as a single object, preventing reuse.
  • Traversing the scene graph is slow and cumbersome.
  • Compression is complex without specialized tools.

gltfjsx addresses these by:

  • Creating a JSX component with a virtual graph of nodes and materials.
  • Pruning unnecessary nodes for better performance.
  • Compressing models (up to 70%-90% size reduction) with Draco, texture resizing, and WebP conversion.

Setup

  1. Ensure your project uses @react-three/fiber (>= 5.x) and @react-three/drei (>= 2.x).
  2. Place your .glb file in the /public folder of your project.

Usage

Run the following command to process your GLB file:

npx gltfjsx public/models/model.glb --transform

This generates:

  • A Model.jsx file with a React component.
  • A compressed model-transformed.glb file in the /public folder.

Example output (Model.jsx):

import { useGLTF, PerspectiveCamera } from "@react-three/drei";

export function Model(props) {
  const { nodes, materials } = useGLTF("/model-transformed.glb");
  return (
    <group {...props} dispose={null}>
      <PerspectiveCamera
        name="camera"
        fov={40}
        near={10}
        far={1000}
        position={[10, 0, 50]}
      />
      <pointLight
        intensity={10}
        position={[100, 50, 100]}
        rotation={[-Math.PI / 2, 0, 0]}
      />
      <group position={[10, -5, 0]}>
        <mesh geometry={nodes.robot.geometry} material={materials.metal} />
        <mesh geometry={nodes.rocket.geometry} material={materials.wood} />
      </group>
    </group>
  );
}

useGLTF.preload("/model-transformed.glb");

Using the Component

import { Canvas } from "@react-three/fiber";
import { Model } from "./Model";

function App() {
  return (
    <Canvas>
      <Model position={[0, 0, 0]} />
      <Model position={[10, 0, -10]} />
    </Canvas>
  );
}

Benefits

  • Reuse: The component can be reused multiple times with minimal draw calls.
  • Dynamic Modifications: Easily change materials, add events, or make parts conditional:
    <mesh
      geometry={nodes.robot.geometry}
      material={materials.metal}
      material-color="green"
      onClick={handleClick}
    />
  • Compression: The --transform flag applies Draco compression, texture resizing (default: 1024x1024), and WebP conversion, reducing file size significantly.

Additional Options

  • --types: Adds TypeScript definitions for type safety.
  • --instance: Instances recurring geometry for fewer draw calls.
  • --draco: Uses local Draco binaries (place in /public).
  • --resolution: Adjusts texture resolution (e.g., --resolution 512).

Read more on gltfjsx GitHub.

Conclusion

Compressing GLB files is essential for performant Three.js applications. For vanilla Three.js, the gltf-transform script provides a flexible way to optimize models with Draco compression and texture resizing. For React Three Fiber users, gltfjsx with the --transform flag simplifies the process by combining compression with JSX component generation, enabling reusable and dynamic 3D components.

Try both approaches based on your project needs, and always test the compressed models to ensure visual quality meets your requirements. For more advanced optimizations, explore additional gltfjsx flags like --instance or --types.

How is your experience so far?

ProjectsAboutResumeDesignBlog3D StuffContact BlogFreelanceLegacySite MapGithubLinkedin

Available
For
Work

© 2020 - 2025 Brandon Nolan. All Rights Reserved.

+1(437)-439-3888
Canada | Global