Loading Assets
Your custom components and behaviors can load external assets like images, audio files, or 3D models. Mattercraft provides two approaches depending on whether the asset is fixed or can change at runtime.
Constructor Props with ProjectFile
Section titled “Constructor Props with ProjectFile”Use a constructor property with the ProjectFile type when the asset is set once and doesn’t change during the experience. The Mattercraft editor will show a file picker that filters to matching files in your project.
import { ContextManager, ProjectFile, zComponent, zLoad } from '@zcomponent/core';import { Group } from '@zcomponent/three/lib/components/Group';import * as THREE from 'three';
interface ConstructorProps { /** * The image to display */ source: ProjectFile<'*.+(jpg|jpeg|png|webp)'>;}
@zComponent({ icon: 'texture' })export class TexturedSphere extends Group { private _material: THREE.MeshStandardMaterial;
constructor(contextManager: ContextManager, private constructorProps: ConstructorProps) { super(contextManager, constructorProps);
this._material = new THREE.MeshStandardMaterial();
const mesh = new THREE.Mesh( new THREE.SphereGeometry(), this._material );
this.element.add(mesh); }
@zLoad() private async _load() { const loader = new THREE.TextureLoader(); const texture = await loader.loadAsync(this.constructorProps.source); texture.colorSpace = THREE.SRGBColorSpace; this._material.map = texture; }
dispose() { this._material.map?.dispose(); this._material.dispose(); return super.dispose(); }}The ProjectFile<'pattern'> type accepts a glob pattern to filter which files appear in the picker:
| Pattern | Matches |
|---|---|
*.+(jpg|jpeg|png) |
Image files |
*.+(mp3|wav) |
Audio files |
*.glb |
3D model files |
The @zLoad() decorator registers the method as a loadable process - the loading screen will wait for it to complete before the experience starts.
Runtime Props with File Values
Section titled “Runtime Props with File Values”Use a runtime property with @zUI({ values }) when the asset can be changed during the experience. This is useful for swappable textures or configurable media.
import { ContextManager, zComponent, zObserve, zUI } from '@zcomponent/core';import { Group } from '@zcomponent/three/lib/components/Group';import * as THREE from 'three';
@zComponent({ icon: 'texture' })export class SwappableTexture extends Group { private _material = new THREE.MeshStandardMaterial();
constructor(contextManager: ContextManager, constructorProps: {}) { super(contextManager, constructorProps);
const mesh = new THREE.Mesh( new THREE.SphereGeometry(), this._material );
this.element.add(mesh); }
@zUI({ values: 'files *.+(jpg|jpeg|png)' }) @zObserve((url, instance: SwappableTexture) => { if (url) { const loader = new THREE.TextureLoader(); loader.load(url, texture => { texture.colorSpace = THREE.SRGBColorSpace; instance._material.map = texture; instance._material.needsUpdate = true; }); } }) public textureUrl?: string;}Runtime file properties load asynchronously after the experience has started, so they won’t delay the loading screen.
Bundled Assets
Section titled “Bundled Assets”For assets that ship with your component (not user-configurable), use the standard import.meta.url pattern:
const textureUrl = new URL('./textures/default.png', import.meta.url).href;
@zLoad()private async _load() { const loader = new THREE.TextureLoader(); const texture = await loader.loadAsync(textureUrl); this._material.map = texture;}This bundles the asset with your script, making it available without any user configuration.
When to Use Each Approach
Section titled “When to Use Each Approach”| Approach | Use When |
|---|---|
Constructor prop with ProjectFile |
Asset is required and set once (e.g., a component’s primary texture) |
Runtime prop with @zUI({ values }) |
Asset is optional or can be swapped during the experience |
Bundled with import.meta.url |
Asset ships with your component and isn’t user-configurable |
For user-facing components, constructor props are usually the best choice - they’re loaded before the experience starts and integrate with the editor’s file picker.