What glTF guarantees, and what it does not
glTF is an open format from the Khronos Group for moving 3D assets between tools and engines, and it serves as the open standard format for web 3D assets (The Khronos 3D Formats Working Group, 2021). What the format guarantees is interoperability: a file that conforms to the specification can be exchanged between tools. It does not guarantee that the content is right. A mesh with missing faces, a node exported twice, and a part with the wrong name are all still valid glTF.
The Khronos glTF-Validator checks JSON, GLB, buffers, and textures against the glTF 2.0 specification. It comes as a browser version, a command-line tool, and an npm package, so it can run inside automation (Khronos Group, glTF-Validator). Putting it at the first gate of intake is a sensible use. That use is a recommendation of this article, and the validator's own page does not describe an intake process. The validator answers whether a file conforms to the specification. It does not answer whether the content is right for the product. Missing faces, extra nodes, and parts whose names do not match the binding rules are content problems, and the comparisons at later gates have to catch such problems.
For product-oriented 3D content, Khronos 3D Commerce publishes a separate set of asset creation guidelines, currently version 2.0 from 2025. Its stated goal is to make photorealistic digital twins of products usable on the web, in apps, in games, and in XR (Khronos Group 3D Commerce, 2025). When agreeing on specifications with a modeler, the public guidelines can serve as a shared reference for both sides, with the original text as the authority on details.
Files from the modeler are raw material, and production files must be reproducible from source
Files from an outside modeler commonly arrive with missing faces, duplicated exports, stray nodes, or edits made by a previous hand. If such a file is copied straight into production, nobody can later explain how the production version came to be, and when the picture goes wrong there is no obvious place to start looking. The development team therefore keeps a single rule: nothing is copied straight into production.

Every production file records three things: its source, its output, and its parameters. A separate command verifies that the production file can be reproduced from the source. The reproduction check compares structure and bounding boxes, not bytes. Compression rewrites bytes and shifts node world coordinates as well, so comparing either one raises false alarms across the whole batch. What a modeler changed is checked the same way, with a source-versus-production diff script whose only deciding quantity is the bounding box.
Three general rules apply as well.
- Before recompressing any asset, compare the material flags of the online version against the original file. Recompressing from the original file washes out material settings that had been fixed by hand on the online version.
- Never modify a GLB by reading and writing the whole file through a graphics library. The geometry gets re-encoded, and the file can grow to about 1.4 to 2.9 times its original size.
- To change a texture in place, edit at the byte level: the file size stays the same and every other byte is left untouched.
- about 5.0 MBModel oneWhole-file rewriteabout 9.1 MB
- about 4.8 MBModel twoWhole-file rewriteabout 14.1 MB
- about 6.6 MBModel threeWhole-file rewriteabout 9.3 MB
File sizes of three GLB files before and after a whole-file read and write through a graphics library, rounded to approximate values
Compare item by item at intake, so a bad picture can be traced to the asset or the pipeline
The first intake pass checks three things: missing faces, duplicates, and extra nodes. In one intake, one color variant of the source model had a part missing about 270 triangles, and the texture coordinates of about 60 more triangles were stretched. The fix touched geometry only. Not one byte of the texture changed.
When fragments or holes show up on screen, the development team separates the asset from the pipeline by elimination. The order is fixed, and each step rules out exactly one possibility.
- View the compressed and the full versionIf both show the defect, compression encoding is not the cause
- Show textures alone, then switch textures offRules out the textures
- Switch to back-face displayIf the far side is visible, the surface is missing there: a hole in the geometry
- Open the source file directly in BlenderRules out the development team's own processing
- Compare the same-named part across two colors of one modelShows whether the defect appears in only one color
- The defect appears when the source file is read directlyJudge it an asset problemReturn it to the modeler with the position and the count
- The source file is clean and only the production file is wrongJudge it a pipeline problemThe development team regenerates from source and compares again
- Colors after compression differ from the sourceCheck compression first, not the assetDecode back and compare with the source, texture by texture
Comparing the same-named part across two colors of one model shows whether the defect lives in only one color. When the cause lands on the asset side, the return list to the modeler carries the position and the count. When it lands on the pipeline side, the development team fixes it without using up the modeler's time.
After compression, decode back and compare with the original, on every file
The way to verify texture compression is to decode the compressed file back to full resolution and compare it with the source image, one by one, across the whole set. The KHR_texture_basisu extension describes a KTX v2 container combined with Basis Universal supercompression, in which the engine transcodes the universal format into a block-compressed format that the platform supports (Callow et al., 2020). Whether the colors survive transcoding is something the intake side has to measure on its own.
After product color textures are converted to a GPU compression format, individual colors can look gray, and the instinct is to judge the asset broken and ask the modeler to re-export. Decoding the source and the compressed file at full resolution and comparing average color shows that the low-bitrate ETC1S format had pulled the color endpoints of individual textures toward gray. Re-encoding the same source in the higher-quality UASTC format restored the color, at a cost of only about 30 KB per texture. The other colors in the same batch were fine under ETC1S, so the effect shows up texture by texture, and sampling would not reveal it.
The verification itself has two traps. Decoding must happen in a clean, separate folder and use the largest mip level; otherwise a filename wildcard picks up the wrong level and produces false numbers. The decompression command also emits several variants, and feeding in the wrong one as alpha makes a whole set of colors verify as white-gray. Sampling only a handful of textures, meanwhile, happened to skip the one with the problem. The conclusion is that a full comparison is just a loop, and there is no reason to sample.
Textures with transparency always take the higher-quality format, because the low-bitrate format encodes alpha as a separate slice and cut-out edges break apart. In the other direction, solid-color textures should shrink. GPU usage is set by resolution, not by file size on disk, so a solid-color texture that keeps a high resolution can compress to almost nothing on disk and slip past every audit that looks only at file size. The deciding quantity is the per-channel standard deviation after decoding, alpha included. A standard deviation of 0 means a solid color, and the texture can shrink to 4×4 with no visible change; anything else is real image content and must be left alone.
How the memory budget and the first-screen load are controlled is a separate topic, covered in Web 3D performance engineering.
Deforming parts: no second quantization, and rebuild in code when repairing the data never ends
Parts that deform with a skeleton (skinned meshes) need special handling. Some files from a modeler arrive already compressed. If the pipeline decodes such a file and compresses it again at the default bit depth, that is a second quantization: normals get pushed onto a different grid, the part visibly warps, and it looks as if it were not bound to the skeleton. So skinned parts are never quantized a second time. Instead the original file is served as it is, and the compression script excludes such parts.
Verifying a skinned part cannot rely on the mesh bounding box, which describes the flattened bind pose. The check measures bone world positions instead.
A different problem is a seam where one segment breaks off. The original file holds the part as one continuous strip of mesh, and the pitch of the texture cells does not equal the pitch of the bones. There are three common ways of repairing the data: restoring the collapsed cells, rebinding to the nearest segment, and shifting the whole run to the median. The result is often one cell fixed and a hole opened in another, and measuring by cell-center distance can even produce phantom gaps that do not exist.
A steadier approach is to stop repairing data and write code: an instanced mesh of rigid units placed at equal spacing along the bone path, with UVs and width measured from the original mesh. The original file stays as it came. Any new style afterward works as long as the bones trace the path correctly. For outsourced models, repairing data can turn into a bottomless pit, since every style breaks in a different way. Written as code, what gets repaired is the rule, not the individual case.
3D failures are silent, so acceptance looks at numbers, not screenshots
Problems in a 3D configurator often raise no error. The console is clean, the automated tests are all green, and the picture looks fine, yet one part did not follow the fold, or sits 2 cm off. The reason is that putting one model item live means connecting four layers: GLB node names, binding rules, data, and the front-end list. Miss any one of the four and the result is zero errors and a part that is simply not there.

In the acceptance table used on the LIDROP 3D configurator, every line states both what counts as evidence and what does not. The overall approach is described in how the LIDROP 3D configurator was built, and the project itself in the LIDROP case.
| Acceptance item | Counts as evidence | Does not count |
|---|---|---|
| Geometry intact | World bounding-box offset measured after decoding, with a tolerance below 0.01% | The min and max values on the accessor: quantization makes those values report a huge offset that is not real |
| Part in the right place | Relative measurement against the mount point, mesh by mesh | The bounding box of a category, which distant child parts contaminate; and agreement among styles, since every style can drift together in neat step |
| Fold really moves | The summed bone world displacement before and after the fold, plus a track-by-track comparison of the two animations | The number of animations: a changed count may be only a duplicated export |
| Behavior snapshot | Differences fall only on the parts that were touched; the baseline file and the code change in the same revision, with each field explained | A baseline file updated on its own, with no explanation for each field |
A screenshot diff does not count as evidence. Several pieces of code write to the scene camera, so the whole image changes anyway, and a difference cannot be tied to any one change. Two more checks apply: the number of materials and textures must equal that of the online version, and when a product has several styles, every style is verified and leaves its own evidence. The checks are bundled into one command that runs 7 checks at a time, covering node binding, camera, types, and behavior snapshots, among others.
When only a scan, an old model, or CAD exists
If the modeler can prepare the colorways in advance, glTF has a place for that: the KHR_materials_variants extension keeps multiple sets of prebuilt material variants in a compact form within one asset, with one set active at a time at runtime (Bond et al., 2018–2020). At intake, the same-named parts across variants must line up with each other, which is why the process builds in a comparison between two colors of one model.
When a client has only a single scanned model, part-level recoloring is still possible. A scan like this may have material granularity that is too coarse, with one material covering several parts. The development team splits the model by triangle position at load time: it computes the world-space centroid of each triangle, and triangles that fall inside a category box are regrouped into separate geometry groups, each with a dedicated material. For about 190,000 triangles this takes about 0.2 seconds, and it runs only once, at load time.
For recoloring, the material is copied under the name "original-name@category-code" and only the tagged copies are tinted, so parts that shared one original material do not affect each other. The split has a knock-on effect, though. Two parts that originally shared one material are separated, and one part ends up on a new tagged material. A rule that matches by material name normally strips the tag first, so the unrelated other part gets matched too and receives the same effect. That name therefore gets an exception that recognizes only untagged materials.
When the client has CAD, the key is to define the scope of the conversion before work starts. LIDROP received an exploded view from CAD and asked for the web version to expand and reassemble in the same sequence. The original file's animation segments parsed into 52 steps, mapped to a 31-segment timeline on the web; progress is a single variable, and camera framing, labels, and axis lines all read only that. Before work began, the development team put the parts that would not carry over to the web version into a plain written list: placeholder steps with 0 displacement, sub-parts inside an assembly that have no individual displacement, and cables that are hidden entirely during the explosion. That way acceptance holds no surprises. The assembled state matched the pre-change images pixel for pixel.
When only an old model, a scan, or CAD exists, judging the current state first and choosing the method afterward falls within 3D interactive development.
What the modeler, the development team, and the client each own
The line of responsibility is drawn first, so that problems are not passed back and forth later. Defects in the asset itself are the modeler's to fix by resupplying or re-exporting. Reproducing the production file and acceptance belong to the development team. The client decides which parts will change, and decides whether to rebuild when the modeler does not provide source files. Who owns what between modeler and developer is also something to settle when evaluating a software vendor, as covered in how to choose a software vendor.
Division of work in the process described here
When this does not fit
- Only a single static render is needed. Intake, reproduction, and acceptance exist for repeated releases and new versions, and one image has no use for the process.
- The model will be used once and never revised. The cost of reproduction and comparison is paid back through the debugging time saved at each new version.
- The modeler provides no source files and there is no budget to rebuild. The production file cannot be traced to a source, so reproduction is not possible, and the source problem has to be handled first.
What to prepare first
- The existing model files or CAD files, with source files included where available.
- The modeler's details, including the specifications originally agreed and whether the source files can be obtained.
- The list of parts to be changed.
Once these are in hand, describe the current state of the model and talk it through with the development team.




