MaterialX Needs a Single-File Container

Back to Blog Listing

MaterialX Needs a Single-File Container

MaterialX assets are multiple loose files, which breaks down in real pipelines. The community has already solved this with zip packaging. We just need to formalize it as .mtlz.

Ben HoustonApril 24, 20267 min read

At Land of Assets, one of the pillars of our pipeline is standardizing on MaterialX and OpenPBR for material interchange.

That decision has been great for collaboration. But in production, it exposes a practical gap: how do we deliver a MaterialX material as one portable, self-contained file?

The Problem#

A typical MaterialX asset is a .mtlx file alongside multiple texture files, all wired together by relative paths that break the moment anything is moved or renamed. That structure is fine in a local DCC project folder. It is painful everywhere else:

  • No single file to upload, download, version, or hash
  • Broken relative paths after moves or renames
  • Complex multi-file APIs and brittle CDN behavior
  • No OS or browser recognition — there is no way to say "open this material in a viewer"

For real collaboration, marketplace distribution, and API-driven delivery, you need one file per material.

The Community Already Solved This#

Poly Haven and the AMD Material Library — two of the most widely used public MaterialX repositories — both distribute materials as .mtlx.zip archives. This works well, and it has driven real-world convergence on a consistent internal layout:

MaterialX zip layout from PolyHaven and AMD Material Library

  • One .mtlx file at the root of the archive, named after the material
  • All textures under /textures/
  • Other resources (node definitions, includes) in subdirectories

Keeping the root clear of everything except the primary .mtlx file is what makes the layout unambiguous — any tool can identify the entry point without guessing.

This convention did not emerge from a committee. It emerged from practice. The demand is clear and the solution is already proven.

The Missing Piece: A Dedicated Extension#

The one thing .mtlx.zip cannot provide is first-class recognition. Every operating system and browser sees it as a generic zip file — because it is. There is no way to register a MIME type for it, no way to associate it with a material viewer, and no way for an API or CDN to distinguish a material package from any other archive.

A dedicated extension fixes all of that at no cost to the format itself.

The .mtlz Format#

.mtlz is a standard zip archive following the conventions already established by Poly Haven and AMD. We are not inventing a new format — we are naming and formalizing what the community built organically.

Layout#

marble-cliff.mtlz
├── marble-cliff.mtlx      ← exactly one .mtlx file, at the root
└── textures/
    ├── marble_cliff_diff.jpg
    ├── marble_cliff_nor_gl.exr
    ├── marble_cliff_rough.exr
    └── marble_cliff_disp.png

Normative rules#

  1. The archive is a valid zip file.
  2. Exactly one .mtlx file appears at the root level. Its name can be anything, but this name may be used in UIs for the material's name, so it is best to name it something meaningful, rather than "material.mtlx".
  3. All texture files are stored under subdirectories, such as /textures/.
  4. All other referenced resources (node definitions, includes, etc.) are stored in subdirectories, never at the root.
  5. The file extension is .mtlz.
  6. The MIME type is model/materialx+zip.

The model/materialx+zip MIME type follows the structured syntax convention used across the web — the same +zip suffix pattern seen in model/vnd.usdz+zip — and extends naturally from the existing model/materialx type registered for .mtlx files.

Metadata#

MaterialX has no built-in fields for author, license, or provenance on the root <materialx> element. The format's <attributedef> mechanism — designed exactly for standardized extensions — fills that gap cleanly: declarations are self-describing, and tools that do not understand them will, per the MaterialX spec, preserve and re-emit them unchanged.

We propose the following metadata fields as part of the .mtlz convention, using the mtlz_ prefix to avoid collisions with other tools' custom attributes:

<?xml version="1.0"?>
<materialx version="1.39" colorspace="lin_rec709"
  mtlz_name="Marble Cliff"
  mtlz_authors="Ben Houston ([email protected]), jcaron"
  mtlz_license="CC0-1.0"
  mtlz_license_url="https://creativecommons.org/publicdomain/zero/1.0/"
  mtlz_source_url="https://example.com/materials/marble-cliff"
  mtlz_version="1.0.0"
  mtlz_description="A weathered marble cliff face with natural veining and displacement."
  mtlz_keywords="marble, cliff, rock, natural, displacement, tiled">

  <attributedef name="mtlz_name"        attrname="mtlz_name"        type="string" value="" elements="materialx"/>
  <attributedef name="mtlz_authors"     attrname="mtlz_authors"     type="string" value="" elements="materialx"/>
  <attributedef name="mtlz_license"     attrname="mtlz_license"     type="string" value="" elements="materialx"/>
  <attributedef name="mtlz_license_url" attrname="mtlz_license_url" type="string" value="" elements="materialx"/>
  <attributedef name="mtlz_source_url"  attrname="mtlz_source_url"  type="string" value="" elements="materialx"/>
  <attributedef name="mtlz_version"     attrname="mtlz_version"     type="string" value="" elements="materialx"/>
  <attributedef name="mtlz_description" attrname="mtlz_description" type="string" value="" elements="materialx"/>
  <attributedef name="mtlz_keywords"    attrname="mtlz_keywords"    type="string" value="" elements="materialx"/>

  <!-- material graph ... -->
</materialx>
FieldFormatNotes
mtlz_namestringHuman-readable material name
mtlz_authorsName (email), NameComma-separated; email optional per author
mtlz_licenseSPDX identifiere.g. CC0-1.0, CC-BY-4.0, MIT; free string also accepted
mtlz_license_urlURLLink to full license text
mtlz_source_urlURLCanonical origin of this material
mtlz_versionSemVere.g. 1.0.0, 2.1.3
mtlz_descriptionstringFree-text description of the material
mtlz_keywordscomma-separated stringsSearch and discovery tags

In the draft .mtlz spec, all of these metadata fields are optional. A valid .mtlz package can omit them entirely. That said, we expect some material library systems may choose to require some subset of these fields for indexing, moderation, provenance, or discovery workflows.

What the extension enables#

Without .mtlx.zipWith .mtlz
OS treats it as a generic archiveOS can associate it with a material viewer
No MIME type for routingmodel/materialx+zip for APIs and CDNs
Browser offers generic zip handlingBrowser can trigger material-specific handling
Ambiguous in material librariesUnambiguous: this file is a material

Path Forward#

At Land of Assets, we are experimentally adopting the draft .mtlz format in parallel with our existing .mtlx.zip community-standard workflow to evaluate how well it performs in a real production scenario. This includes related tools and experiments in the same ecosystem, such as material-viewer.ben3d.ca and material-fidelity.ben3d.ca.

This is explicitly experimental, and we welcome feedback, critiques, and discussion from Poly Haven, AMD, and the broader MaterialX community to help push this initiative forward. The format is already close to what everyone is shipping — this effort is about aligning on a name, a layout, and a metadata convention.

If you are building material repositories, content pipelines, or DCC integrations and this resonates, we would love to hear from you.