Skip to main content

Polyforge

Learn

3D Optimization for the Web

Polygon budgets, texture compression, geometry encoding, and loading strategies. Everything you need to ship 3D assets that load fast and render smoothly on any device.

8 min read
By Polyforge · Published Mar 21, 2026 · Updated Mar 28, 2026
LEARN
Optimization Guide

Overview

A character exported from Blender might weigh 80 MB with 4K textures and 500,000 triangles. On a mobile connection, that takes 20 seconds or more to download. This guide covers the techniques professionals use to shrink 3D assets for web delivery without visible quality loss.

Every byte in a 3D file falls into one of three buckets: geometry (vertices, normals, UVs), textures (color maps, normal maps, roughness maps), and metadata (scene graph, materials, animations). Textures account for 60% to 90% of total file size. Geometry makes up most of the rest.

$ contents
$ why_size_matters

Why File Size Matters

Web performance depends on three things: download speed, parse time, and rendering cost. A 3D file affects all three.

Download speed is the most obvious bottleneck. A 50 MB model can take 8 seconds or more on a typical mobile connection, and real conditions with packet loss and congestion make it worse. Users leave if they have to wait.

Parse time adds to the delay. The browser must decode geometry, decompress textures, and build the scene graph before anything appears on screen. Larger files take longer to parse.

Rendering cost is ongoing. Every frame, the GPU processes all visible geometry and textures. Oversized assets cause dropped frames, which makes the experience feel broken on less powerful devices.

Google recommends keeping interactive 3D assets under 15 MB for e-commerce. Shopify enforces the same guideline. For mobile AR, Apple and Google both target files under 10 MB.

$ polygon_budgets

Polygon Budgets

The triangle count of a model determines how much geometry data the GPU must process each frame. More triangles means a larger file and a heavier rendering load.

These budgets reflect real device testing across mobile and desktop browsers:

Context Triangle Budget Typical Use
Single product viewer 50K to 100K E-commerce, configurators
Full 3D scene 100K to 300K Virtual tours, room planners
Background props 500 to 1,000 each Environmental details
Mobile AR 50K to 100K total Quick Look, Scene Viewer

Flat surfaces waste triangles. A perfectly flat wall modeled with 10,000 triangles looks identical to one with 2 triangles. Focus polygon density on curved surfaces, silhouette edges, and areas the viewer will examine up close.

Blender's Decimate modifier, MeshLab's quadric edge collapse, and gltf-transform's simplify function all reduce polygon counts programmatically. Manual retopology in tools like ZBrush or Blender produces better results but takes more time.

$ textures

Texture Optimization

Textures account for 60% to 90% of most 3D file sizes. They also consume the most GPU memory at runtime.

A single 4096x4096 PNG texture with an alpha channel uses roughly 64 MB of GPU memory when loaded, regardless of the compressed file size on disk. That 2 MB PNG file decompresses to 64 MB the moment it reaches the GPU.

Dimension guidelines for web delivery:

  • Hero assets (product shots, main characters): 2048x2048 or 4096x4096
  • Secondary objects: 1024x1024
  • Background elements: 256x256 to 512x512

Always use dimensions that are powers of two: 256, 512, 1024, 2048, or 4096. Textures at other sizes cannot generate mipmaps in WebGL, which causes blurry rendering at distance and wastes memory.

Reducing a texture from 4096x4096 to 2048x2048 cuts both file size and GPU memory by roughly 75%. If the model displays at 400 pixels wide on screen, 4096 resolution provides zero visible benefit.

Use JPEG for color maps (baseColor) and PNG only when transparency is required. JPEG at quality 85 produces smaller files than PNG with no perceptible difference in most materials.

$ gpu_textures

GPU Compressed Textures (KTX2)

Standard JPEG and PNG textures decompress to raw pixels when the GPU loads them. A 200 KB JPEG texture can occupy 20 MB or more of video memory.

KTX2 with Basis Universal solves this problem. These textures stay compressed in GPU memory, using roughly one tenth the memory of uncompressed equivalents. At runtime, the browser transcodes them to the device's native format: BC7 on desktop GPUs, ASTC on modern mobile chips, or ETC2 on older Android devices.

Two compression modes are available:

ETC1S produces the smallest files. It works well for solid color textures, UI elements, and stylized content. Quality suffers on photorealistic materials and detailed normal maps.

UASTC produces larger files but matches BC7 quality. Use it for PBR materials, normal maps, and any texture where fine detail matters.

Real results from the Khronos KTX2 artist guide: a stained glass lamp model dropped from 96 MB to 21 MB of GPU memory (78% reduction) with minimal visual difference.

KTX2 saves memory, not just bandwidth. Unlike JPEG and PNG, KTX2 textures stay compressed on the GPU. The savings compound in scenes with many materials, making it essential for product configurators, room planners, and any multi-asset viewer.
$ geometry_compression

Geometry Compression

Two compression systems dominate the glTF ecosystem: Draco (by Google) and meshopt (by Arseny Kapoulkine). Both reduce geometry data significantly, but they work differently. Both require your model to be in GLB or glTF format. If you are working from FBX, you will need to convert FBX to GLB before applying compression. If your source is OBJ, convert OBJ to GLB first to unlock the full glTF compression pipeline.

Feature Draco meshopt
File size reduction 70% to 95% 50% to 91%
Morph targets Not supported Supported
Animation data Not supported Supported
Decode speed Moderate 1 to 3 GB/s (faster)
Vertex ordering Reorders vertices Preserves GPU cache order
Decoder size ~150 KB gzipped (WASM) ~7 KB gzipped (WASM)
Precision control 6 to 16 bit quantization Byte level quantization

Draco achieves higher compression ratios on static models because it uses aggressive quantization and entropy coding. Its Edgebreaker algorithm restructures mesh connectivity data for maximum compression. The default 11 bit quantization introduces no visible quality loss on most models.

meshopt takes a different approach. It optimizes vertex and index data for GPU cache locality, then applies byte level compression. The result is smaller than raw data and faster to render because the GPU processes vertices in an efficient order.

For web delivery, the practical difference: Draco produces smaller files but needs a larger decoder and more CPU time to decompress. meshopt produces slightly larger files but decodes faster and renders more efficiently.

$ draw_calls

Draw Calls and Scene Structure

A draw call is a command from the CPU to the GPU to render a batch of geometry. Every unique combination of mesh and material creates at least one draw call. The GPU processes draw calls sequentially, so too many of them bottleneck the CPU before the GPU even becomes the limiting factor.

Target fewer than 100 draw calls per frame for smooth 60 FPS on mobile devices. Desktop can handle more, but keeping draw calls low benefits all platforms.

Strategies for reducing draw calls:

  • Merge meshes that share the same material. Ten boxes with the same material can become one mesh with one draw call instead of ten.
  • Use texture atlases instead of separate textures per object. Packing 20 textures into one atlas lets you merge the meshes because they now share a single material.
  • InstancedMesh in three.js renders thousands of identical objects (trees, particles, bolts) in a single draw call.
  • BatchedMesh (three.js r159 and later) combines different geometries sharing materials into one call.
$ loading_strategy

Loading Strategy

Never load the 3D engine when the page first opens. three.js alone adds over 600 KB to the JavaScript bundle. Combined with Draco and other decoders, the total can exceed 5 MB of JavaScript.

The correct approach:

  • Serve static HTML with the page layout. No 3D JavaScript loads at this stage.
  • When the user interacts (drops a file, clicks a button), dynamically import the 3D library.
  • Load WASM decoders only when a specific operation needs them. The Draco decoder loads only if the model uses Draco compression.
  • Show a progress indicator while assets download.

For scenes with multiple textures, load geometry first and stream textures in the background. The model appears immediately with placeholder colors, and details fill in as textures arrive. This technique works naturally with the glTF format since it stores geometry and textures as separate resources.

Compress as the final step. Run Draco or meshopt compression at the very end of your pipeline. Lossy compression applied repeatedly degrades quality. Keep uncompressed originals in your project and compress only for distribution.
$ faq

Frequently Asked Questions

How many polygons can a web browser render?

Desktop browsers on modern hardware render 1 million or more triangles at 60 FPS without difficulty. Mobile browsers handle 100,000 to 300,000 triangles comfortably. The practical limit depends on the GPU, texture load, shader complexity, and number of draw calls.

Should I use Draco or meshopt compression?

Use Draco for static models where minimum file size is the priority. Use meshopt for animated models, morph targets, or when decode speed matters. Both compress geometry by 50% or more. Many production pipelines apply meshopt for vertex reordering and Draco for final compression.

What texture resolution works best on mobile?

2048x2048 is the practical maximum for mobile web. Use 1024x1024 for secondary objects and 512x512 for background elements. Always test on actual devices because simulator performance does not reflect real GPU constraints.

Does optimization reduce visual quality?

Properly executed optimization is invisible. Draco at 11 bit quantization produces no perceptible distortion. Texture downscaling from 4096 to 2048 is unnoticeable in viewports under 500 pixels wide. Polygon reduction of 30% to 50% on organic models preserves visual fidelity when done with algorithms that preserve edges.

What file size should I target for web 3D?

Under 5 MB for mobile experiences and under 15 MB for desktop. Google and Shopify both recommend these thresholds for interactive 3D content. After Draco compression and texture optimization, most single model assets fit within 1 to 5 MB. Polyforge can help you reach these targets with its built-in optimizer.

$ related

Related Tools