Get Lifetime Access Before The Timer Hits Zero 👉23 days12 hours00 min08 secClaim Deal - Just $197!
Getting started

Setting up the Draco + KTX2 decoders

Configure Draco for compressed geometry and add KTX2 support only when a model uses compressed textures.

AP
Aron Prins
Founder · Updated Aug 11, 2026 · 2 min read
On this page (5)

Catalog GLBs use Draco-compressed geometry, so they need a DRACOLoader. A model needs KTX2Loader only when it contains KTX2/Basis textures. Many low-poly packs use vertex colours and do not need the texture transcoder.

Copy the decoder files#

Serve decoder files from your own application. Copy them from the same three.js version your project uses:

copy-decoders.sh
mkdir -p public/draco public/basis

cp -R node_modules/three/examples/jsm/libs/draco/gltf/. public/draco/
cp node_modules/three/examples/jsm/libs/basis/basis_transcoder.* public/basis/

Your application can now load them from /draco/ and /basis/. threejsassets provides downloadable models and packs, not hosted decoder or asset URLs.

Draco-only models#

Use this setup for texture-free or otherwise non-KTX2 models:

load-draco.ts
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";

const draco = new DRACOLoader().setDecoderPath("/draco/");
const loader = new GLTFLoader().setDRACOLoader(draco);

loader.load("/barrel-01.glb", (gltf) => {
  scene.add(gltf.scene);
});

Models with KTX2 textures#

Create the KTX2 loader after your renderer exists, then call detectSupport(renderer) before loading the model:

load-draco-ktx2.ts
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
import { KTX2Loader } from "three/examples/jsm/loaders/KTX2Loader.js";

const draco = new DRACOLoader().setDecoderPath("/draco/");
const ktx2 = new KTX2Loader()
  .setTranscoderPath("/basis/")
  .detectSupport(renderer);

const loader = new GLTFLoader()
  .setDRACOLoader(draco)
  .setKTX2Loader(ktx2);

React Three Fiber#

For Draco-only files, pass the decoder path to drei's useGLTF:

Model.tsx
import { useGLTF } from "@react-three/drei";

export function Model({ url }: { url: string }) {
  const { scene } = useGLTF(url, "/draco/");
  return <primitive object={scene} />;
}

For the models that do carry KTX2 textures, attach a configured KTX2Loader through useGLTF's fourth argument, the extendLoader callback. It hands you the GLTFLoader before it runs:

ModelWithKTX2.tsx
import { useGLTF } from "@react-three/drei";
import { useThree } from "@react-three/fiber";
import { KTX2Loader } from "three/examples/jsm/loaders/KTX2Loader.js";

export function Model({ url }: { url: string }) {
  const gl = useThree((state) => state.gl);

  const { scene } = useGLTF(url, "/draco/", undefined, (loader) => {
    const ktx2 = new KTX2Loader()
      .setTranscoderPath("/basis/")
      .detectSupport(gl);
    loader.setKTX2Loader(ktx2);
  });

  return <primitive object={scene} />;
}

The second argument still wires up the DRACOLoader for you; the callback covers KTX2. The component must be a child of <Canvas>useThree only works inside the r3f render tree, and detectSupport needs the live renderer.

Troubleshooting#

  • No DRACOLoader instance provided — attach DRACOLoader before loading the GLB.
  • Missing initialization with .detectSupport(renderer) — call detectSupport after creating the renderer and before loading a KTX2 model.
  • 404 for decoder files — confirm the files exist under the public paths configured above.
  • Model works until three.js is upgraded — copy the decoder files again from the installed three.js version.

See Quickstart for a complete Draco-only example.

Still stuck?

Ask us directly — we answer support mail ourselves.

Contact support
Share
Setting up the Draco + KTX2 decoders — threejsassets.com