Skip to content

Pallets & Assets

DigitalHeaven organizes everything into pallets — packages of related assets like avatars, materials, textures, and models.

A pallet is a folder with a pallet.dh manifest and whatever assets it needs.

Pallets use reverse-DNS identifiers:

  • io.mltn.mltn-mayu
  • tech.azuki.mayu
  • com.example.my-avatar

This prevents naming conflicts and shows ownership. The ID also determines the folder path: io.mltn.mltn-mayu lives at Pallets/io.mltn/mltn-mayu/.

Every pallet needs a manifest:

pallet.dh
{
"id": "io.mltn.mltn-mayu",
"name": "Mayu",
"version": "1.0.0",
"author": "mltn",
"dependencies": {
"tech.azuki.mayu-base": "^2.0.0"
}
}

An asset is any file inside a pallet — definition files (.dh-avatar, .dh-mat, etc.) or binary resources (.png, .glb, .wav).

Within the same pallet — just use the relative path:

skin.dh-mat
{
"texture": "textures/body-diffuse.png"
}

Across pallets — prefix with the pallet ID and a : separator:

avatar.dh-avatar
{
"baseMesh": "tech.azuki.mayu:models/body.glb"
}

This is called a barcode: pallet.id:path/to/asset.

  1. No : in the path → local reference within the current pallet
  2. Has : → everything before it is the pallet ID, everything after is the path
  3. Paths are relative to the pallet root

Assets can inherit from other assets — even across pallets — and override specific properties.

avatar.dh-avatar
{
"inherits": "io.mltn.mltn-mayu:avatar.dh-avatar",
"patches": [
// Only the patches that differ
{
"$type": "dh.set-material",
"target": "Body",
"slot": 0,
"path": "materials/custom-skin.dh-mat"
}
]
}

Inheritance works at every level: avatars, objects, materials, rigs. You only define what’s different.

The compiler resolves all inheritance chains at build time — the compiled output has everything flattened, no unresolved references.

Platform-specific data lives in a platforms/ subdirectory alongside your universal definitions:

my-avatar/
├── avatar.dh-avatar # Universal
├── materials/
│ └── skin.dh-mat # Universal material
└── platforms/
├── vrchat/
│ └── avatar-descriptor.jsonc
├── unity/
│ └── materials/
│ └── skin.dh-mat # Unity-specific shader overrides
└── resonite/
└── avatar-metadata.jsonc

When building for a platform, the compiler loads the universal definition first, then applies any matching overrides from platforms/{platform}/.

Shared assets go in type-based folders at the pallet root. Self-contained features (accessories, outfits) get their own subdirectory with everything co-located:

my-pallet/
├── pallet.dh
├── avatar.dh-avatar
├── textures/ # Shared textures
│ ├── body-diffuse.png
│ ├── body-normal.png
│ └── body-orm.dh-tex
├── models/ # Shared models
│ └── body.glb
├── rigs/ # Shared rigs
│ └── humanoid.dh-rig
├── materials/ # Shared materials
│ ├── skin.dh-mat
│ └── eyes.dh-mat
└── accessories/ # Self-contained features
├── glasses/
│ ├── glasses.dh-obj
│ ├── model.glb
│ ├── material.dh-mat
│ └── lens.png
└── piercing/
├── piercing.dh-obj
├── model.glb
└── material.dh-mat

The rule: shared/base assets in type folders at root; self-contained features get their own directory with everything they need inside.

Designed for Git:

  • Commit: Pallets/ (your source files)
  • Ignore: Build/ (generated output), .dh/ (cache)

The workspace init generates .gitignore and .gitattributes automatically. The .gitattributes file sets up Git LFS for binary assets (.glb, .fbx, .png, .jpg, etc.) — make sure you have Git LFS installed if you’re using those.