How to Inspect a GLB Before Optimizing It
Perform a structured GLB preflight covering validation, bytes, meshes, attributes, materials, textures, animation, transforms, visual truth, and runtime evidence.
The fastest way to waste time on GLB optimization is to change the file before learning what it contains. A 40 MB file dominated by images needs a different plan from a 40 MB file dominated by morph targets. A slow 2 MB model with 90 materials does not need the same treatment as a 2 MB model with one expensive transparent shader.
Inspection turns optimize it into a diagnosis. Preserve the bytes, validate the container, attribute cost by subsystem, and capture the visual and runtime baseline before selecting any transform.
Secure the evidence#
Copy the GLB into an immutable source directory, calculate a SHA-256 or equivalent content hash, and record byte length. Preserve its source URL, creator, license, download or export date, DCC and exporter versions if known, and intended product role.
Never open and resave the only copy before hashing it. DCC import-export can restructure nodes, materials, tangents, images, and animation even when no visible edit was intended.
Create a working directory containing:
- original GLB;
- provenance and license record;
- source hash and byte count;
- validator reports;
- structural reports;
- extracted images only for inspection;
- reference renders;
- runtime capture;
- written target profile;
- later candidates and decision log.
Use content-based IDs so an updated file under the same name does not inherit an obsolete report.
Run format validation first#
Use the Khronos glTF Validator on the untouched GLB. Save machine-readable and human-readable output. Record validator version and options.
Classify findings into errors, warnings, information, and target-profile concerns. Examples include invalid accessor bounds, mismatched buffer lengths, non-unit quaternions, missing min or max values, unused objects, extension declarations, image issues, and animation defects.
A passing result means the file follows checked format rules; it does not mean the model is small, fast, visually correct, or supported by the target runtime. A failing result should normally be repaired or re-exported before lossy optimization.
Check extensionsUsed and extensionsRequired. For every required extension, identify the shipping loader, decoder or transcoder, version, worker configuration, and fallback. An unknown optional extension can change appearance; an unknown required extension can make the file unloadable.
Read the GLB structure#
A GLB contains a header and chunks, normally JSON and binary data. The JSON connects scenes, nodes, meshes, primitives, accessors, buffer views, materials, textures, images, samplers, animations, and skins. The binary chunk contains attribute, index, image, and animation payloads according to those references.
Use a glTF-aware inspector rather than a generic archive tool. Record:
- GLB header and chunk sizes;
- JSON versus binary allocation;
- number of scenes and default scene;
- node hierarchy and maximum depth;
- meshes and primitives;
- accessors, types, component widths, and counts;
- buffer views, targets, alignment, and bytes;
- materials, textures, images, and samplers;
- animations, channels, samplers, and keys;
- skins, joints, inverse bind matrices, and morph targets;
- extensions and extras.
Inspecting the graph also reveals source hygiene: empty transform chains, duplicate meshes, copied materials, hidden high-detail nodes, or multiple scenes that the application did not expect.
Attribute every large byte category#
Create a byte ledger. Assign binary ranges to positions, normals, tangents, UVs, colors, indices, joints, weights, morph targets, animation, images, and other payloads. Avoid double-counting shared buffer views or images.
Calculate percentage of total. If a 24 MB GLB contains 18 MB of images, 4.8 MB of geometry, 800 KB of animation, and 400 KB of structure and padding, texture work has the largest possible leverage.
Compare encoded and decoded cost. A 2 MB JPEG at 4096 × 4096 may decode to about 64 MiB of RGBA pixels at the top level. Network bytes, decoder work, CPU representation, and GPU residency are different ledgers.
Flag unexpectedly small data too. A 32-byte image, zero-count accessor, or animation with a 0-second range can indicate a broken export.
Inspect geometry counts correctly#
Record triangles and other primitive modes per primitive. Convert strips or fans to an equivalent triangle count for comparison if the tool reports them differently. Record index count, position count, and runtime vertex count after attribute splits where available.
List every attribute and component representation. Position alone differs greatly from position, normal, tangent, 2 UV sets, color, 4 joints, and 4 weights. Note normalized integer and quantized data.
Check per-mesh and per-primitive distribution. A total of 100,000 triangles can mean one coherent mesh or 1,000 tiny primitives. The latter adds scene and draw overhead.
Report degenerates, duplicate triangles, zero-length normals, non-finite values, boundary edges, disconnected components, and non-manifold regions using a geometry-capable tool. Not every issue requires repair for rendering, but it should be understood.
Visualize density. Micro-triangles on flat regions and dense hidden internals are high-confidence candidates; silhouette and joints need more caution.
Inspect vertex duplication causes#
Compare unique positions with stored vertex records. UV seams, hard normals, material boundaries, colors, skin weights, and morph data can split vertices.
If 50,000 triangles use 90,000 runtime vertices, inspect whether every edge is hard, UV islands are excessively fragmented, or primitives divide the surface. Do not weld blindly: seams and hard edges can be required for correct appearance.
Check 16-bit versus 32-bit indices and whether the runtime or optimizer will split or merge primitives. Quantization and index changes can reduce bytes but affect compatibility and precision.
Look for unused attributes by comparing material and application requirements. Tangents may be removable without a tangent-space normal map; UV1 may be necessary for lightmaps; vertex colors may drive masks not obvious in a neutral viewer.
Document the proposed consumer before deleting an unfamiliar stream.
Audit materials and draw structure#
List material name, shader model, base-color factor and texture, metallic and roughness inputs, normal, occlusion, emissive, alpha mode, alpha cutoff, double-sided flag, texture transforms, and extensions.
Map primitives to materials. Count duplicate materials with identical parameters and textures. Count unused materials and images. Estimate base draws and additional shadow, depth, outline, or transparency passes in the actual renderer.
Inspect alpha. A material can be marked BLEND even when every alpha value is 1, causing unnecessary sorting and overdraw. MASK and BLEND have different appearance and performance implications.
Check double-sided surfaces. The flag may be intentional for foliage or cloth, or it may hide missing back geometry. Do not globally turn it off without viewing both sides.
Render a material-ID pass. Dozens of tiny islands can reveal an import that duplicated materials per part.
Audit images and texture use#
For every image record name, MIME type, dimensions, channels, bit depth where relevant, encoded bytes, material slots, UV set, sampler, wrap, and color-space expectation.
Calculate decoded top-level and mip-chain estimates. Flag 4096 or 8192 images, unused alpha, duplicate content under different names, strange aspect ratios, and small UV coverage.
Extract images only for diagnosis, preserving the GLB. Inspect base-color baked lighting, normal direction, compression artifacts, alpha boundaries, roughness range, and channel packing. Do not judge scalar data as if it were a color photograph.
Map texel density onto the model. A large image can still look blurry when one important island occupies 4% of it; a 1024 image can be adequate with efficient packing.
Verify KTX2 or other image extensions against the runtime transcoder. Record whether mips are present or generated.
Inspect transforms, scale, and bounds#
Traverse the node hierarchy and compute world transforms. Report translation, rotation, scale, negative determinant, nonuniform scale, and deep chains. Check for both matrix and TRS usage according to glTF rules.
Calculate world-space bounds and physical dimensions. Compare with authoritative data. Identify remote vertices or nodes that inflate framing and culling.
Check origin and forward orientation for the use case. A technically valid centered object may rotate poorly if it should sit on a floor. A root scaled by 0.01 may be intentional conversion or hidden unit debt.
Inspect animation targets before flattening hierarchy. Removing an apparently empty node can break the pivot or animation path.
Test camera near and far clipping against the actual bounds.
Inspect animation, skins, and morphs#
List animation clips, duration, channels, target nodes, interpolation, sampler input counts, and output sizes. Flag constant tracks, duplicate times, extreme sample rates, long empty ranges, and unused clips.
For skins, record joints, hierarchy, inverse bind matrices, maximum weights per vertex, weight normalization, and unused bones. Inspect bind pose and extreme animations.
For morph targets, record count, affected primitives, POSITION, NORMAL, or TANGENT deltas, and byte allocation. Fifty morph targets can dominate geometry even when the base mesh is moderate.
Preview loop boundaries and all clips. The default viewer may play only one and hide broken alternatives.
Never simplify a skinned or morphing mesh as if it were static without a deformation-aware test.
Establish the visual baseline#
Render the untouched GLB from fixed front, side, rear, top, closest, silhouette, and grazing-light views. Use the shipping renderer and a neutral independent viewer.
Capture:
- production-lit color;
- neutral clay shading;
- unlit base color where possible;
- normals or normal visualization;
- depth;
- silhouette mask;
- UV seam or checker view;
- material IDs;
- key animation poses;
- transparent views against light and dark backgrounds.
Record renderer, tone mapping, exposure, field of view, resolution, environment, and camera transforms. A reference is only reproducible when its conditions are known.
Mark must-preserve features and accepted source defects separately. Optimization should not be blamed for a flaw already present.
Measure the runtime baseline#
Load the original through the production delivery path on representative hardware. Measure DNS and connection where relevant, transfer, parse, geometry decode, image decode or transcode, upload, shader compilation, first recognizable frame, interaction ready, and full detail.
Record CPU and GPU frame p50, p95, and p99 during normal interaction. Capture draws, triangles, materials, textures, programs, memory trends, and long main-thread tasks.
Test one instance and production population. Test cold and warm caches. Repeat load and disposal 10–20 times to find retention.
Use controlled toggles: simple material, hidden textures, hidden geometry groups, no shadows, lower resolution, stopped animation. These reveal the limiting subsystem and prevent irrelevant optimization.
Write the diagnosis before changing bytes#
Add explicit stop conditions. If scale is unknown, license is unresolved, required extensions are unsupported, or the source already fails a must-preserve visual view, optimization should pause rather than make the ambiguity cheaper to distribute. Record who can resolve the stop and what evidence is needed.
Summarize findings in priority order with evidence. For example:
- Images are 78% of 24 MB; 4 unused or duplicate 4K maps are the first target.
- Forty-two material primitives create 84 visible and shadow draws; 30 use identical plastic.
- Geometry is 9% of transfer and 1.1 milliseconds of GPU time; aggressive decimation has low value.
- First use stalls 620 milliseconds on shader compilation; prewarming and variant reduction matter.
- Bounds are 12× too large because of one remote component.
Attach proposed actions, expected benefit, visual risk, required approval, and rollback source. This diagnosis becomes the optimization plan.
GLB inspection checklist#
- Source bytes, hash, provenance, license, and exporter context are preserved.
- Validator errors, warnings, and extensions are classified.
- GLB structure and every major byte category are reported.
- Triangles, runtime vertices, attributes, primitives, and topology defects are known.
- Vertex duplication causes are understood before welding or pruning.
- Materials, alpha, double-sided state, passes, and duplicate assignments are mapped.
- Images include encoded, decoded, semantic, UV, and sampler evidence.
- World transforms, physical dimensions, origin, and bounds are verified.
- Animation, skin, morph, and deformation data are inventoried.
- Fixed visual references make source defects and protected features visible.
- Cold, warm, population, frame, and memory baselines use the shipping runtime.
- A ranked diagnosis connects each proposed action to evidence.
Sources and further reading#
- Khronos glTF 2.0 specification
- Khronos glTF Validator
- glTF Transform inspect documentation
- Khronos glTF Asset Auditor
- Three.js documentation: GLTFLoader
Inspection is the first optimization operation because it identifies which information is expensive, broken, unsupported, or valuable. Preserve the source, build byte and runtime ledgers, capture visual truth, and only then choose the smallest set of changes that addresses the measured cause.