All posts
Engineering

Heavy 3D on the Web: How Compression and Edge Delivery Make High-Poly Models Load Instantly

3D on the web has a performance problem — multi-megabyte meshes, blown LCP budgets, and stuttering on mid-range phones. Here's how GLB/Draco compression, smart LODs, and edge delivery solve it, and why an image-based approach sidesteps the WebGL tax entirely.

Meshless Team · June 20, 2026 · 5 min read

Putting 3D on a web page is easy. Putting fast 3D on a web page — the kind that doesn't wreck your Core Web Vitals or stutter on a three-year-old Android — is where most teams get stuck. This is a tour of why 3D is heavy, the techniques that make it lighter, and the architectural decision that sidesteps the whole problem.

Why 3D is heavy in the first place

A raw 3D asset is far more than a picture. It carries:

  • Geometry — vertices, normals, UVs, and the index buffer. A detailed product model can be hundreds of thousands of triangles.
  • Textures — albedo, normal, roughness, metalness, ambient occlusion maps, often at 2K or 4K each. Textures, not geometry, are usually the real bandwidth hog.
  • A runtime — to render any of it in the browser you ship a WebGL engine (three.js, Babylon) and then spend main-thread time parsing, uploading to the GPU, and rendering every frame.

The result: a single "simple" product viewer can mean 5–20 MB over the wire plus a CPU/GPU bill that mid-range devices pay in dropped frames. Your Largest Contentful Paint slips, your page feels heavy, and bounce rate creeps up — exactly the outcome 3D was supposed to prevent.

The compression toolbox

If you're shipping real-time GLB/glTF, these are the levers that matter:

Draco geometry compression

Draco compresses mesh geometry aggressively — frequently 5–10× smaller. The tradeoff is a decode step in the browser (a WASM decoder), which costs a little CPU on load. For geometry-heavy models it's almost always a net win.

# Compress a glTF/GLB with the glTF-Transform CLI
npx @gltf-transform/cli optimize input.glb output.glb \
  --compress draco \
  --texture-compress webp

Meshopt

An alternative (or complement) to Draco with very fast decoding and good ratios. For scenes with many objects or animation data, Meshopt's decode speed can beat Draco's better ratio.

Texture compression — the one people forget

Geometry gets the attention, but textures dominate payloads. Two moves:

  • WebP/AVIF for transport (smaller downloads).
  • GPU formats (KTX2 / Basis Universal) so the GPU stores them compressed in VRAM instead of decoding to raw RGBA — critical on memory-constrained phones.

Levels of Detail (LODs)

Ship a low-poly mesh for the first paint and far viewing distances, swap to high-poly only when the user zooms or the asset is prominent. Less geometry rendered = higher frame rate.

Delivery: compression is only half the story

A 1 MB compressed model still feels slow if it's served from one origin halfway around the world. Edge delivery — caching assets at CDN nodes near the user — collapses round-trip latency so the first byte arrives fast wherever the shopper is. Pair that with Cache-Control immutability and HTTP/2 or HTTP/3 multiplexing and you remove the network as the bottleneck.

The shortcut: don't ship a renderer at all

Here's the architectural insight behind Meshless. For the dominant use case — a product the user wants to inspect from every angle — you don't actually need real-time WebGL. You need the experience of rotating the object. And that can be delivered as a pre-rendered set of images.

Meshless encodes your model once into an optimized 360° frame set (WebP), then serves those frames from the edge. The browser does what browsers are best at: decode and display images. There's:

  • No WebGL runtime shipped to the client.
  • No geometry or texture decode on the main thread.
  • No GPU tax — it runs smoothly on the low-end phones where most traffic lives.
  • Predictable LCP — it loads like an image gallery, because it is one.

The payoff is counterintuitive: a high-poly object can load instantly, because the browser never sees the polygons. Here's a detailed car rim — the kind of high-density industrial part that would be a multi-megabyte mesh in a live WebGL viewer — running with image-gallery performance:

High-poly car rim — heavy geometry, image-fast load

Drag it around. Notice there's no "loading the 3D engine" stall, no jank as it spins. The visual fidelity of a high-poly model with the delivery profile of a photo.

When you do want real-time WebGL

Image-based 360° isn't the answer to everything. If you need free-camera flythroughs, real-time configurator lighting, physics, or AR placement, you want an actual engine — and then the Draco/Meshopt/KTX2/LOD toolbox above is exactly how you keep it fast. The skill is matching the technique to the job: most product inspection is image-based territory; interactive scenes are engine territory.

Getting started

Encoding happens in your browser — the source model never touches our servers — and you get an embed snippet when it's done. The Quick Start walks through it, and Supported Formats lists what you can upload (GLB, STL, and more).

<script src="https://cdn.meshless.io/embed/v1/embed.js" async></script>
<meshless-viewer
  data-project-id="YOUR_PROJECT_ID"
  style="width:100%;aspect-ratio:1"
></meshless-viewer>

Fast 3D on the web isn't magic — it's the right compression, delivered from the edge, with a renderer you ship only when you genuinely need one. See the docs to go deeper, or open the dashboard and benchmark it against your current viewer.