Using Assets (.glb, textures, audio)
Ship models, textures and sounds inside your mod. The host serves them versioned and immutable-cached; the SDK loads them with one hook.
Layout
my-mod/
vibelands-mod.json
client/index.jsx
assets/ ← anything in here ships with the mod
chest.glb
sparkle.png
chime.oggThe hosted sandbox builder bundles assets/ into the signed mod artifact (budget: 25 MB total) and the host serves it at /mods/<id>/<version>/assets/... for hosted releases. Versioned URLs mean hard immutable caching with zero stale-asset bugs after updates.
Loading
import {
useModGltf, // .glb / .gltf - suspends while loading
useModTexture, // images - suspends
useModAudioBuffer, // decoded via the HOST audio context - null until ready
useModAssetUrl, // escape hatch: just the resolved URL string
} from '@vibelands/mod-sdk/client';
export function ChestModel({ position }) {
const gltf = useModGltf('chest.glb');
return <primitive object={gltf.scene.clone()} position={position} />;
}Loaders cache per URL - and the URL embeds your version, so caches roll over cleanly on release.
Audio: always ride the master bus
useModAudioBuffer decodes with the host's AudioContext so playback can route through the master gain - that's what makes your sounds correctly muffle underwater and inside interiors. Requires the audio permission.
import { useModAudio, useModAudioBuffer } from '@vibelands/mod-sdk/client';
export function useChime() {
const audio = useModAudio(); // needs "permissions": ["audio"]
const buffer = useModAudioBuffer('chime.ogg');
return () => {
const ctx = audio.getAudioContext();
if (!ctx || !buffer || !audio.isAudioEnabled()) return;
const source = ctx.createBufferSource();
source.buffer = buffer;
source.connect(audio.getMasterGain()); // ← the important line
source.start();
};
}WARNING
Never new Audio(url) or build your own AudioContext - that audio escapes interior/underwater filtering and the user's volume settings.
Practical limits
| Aspect | Rule |
|---|---|
Total assets/ size | ≤ 25 MB per mod (check and the hosted build enforce it) |
| Formats | .glb (preferred over .gltf+bin), compressed textures, .ogg audio |
| Draco/KTX2 | not provided by the host - ship uncompressed glb or embed your own decoder |
| Paths | relative inside assets/, no .. traversal (the host 404s it anyway) |
| Disposal | loader caches live for the session; prefer .clone() when mounting the same GLTF many times |
Tier A + assets example: a picnic table at spawn
import { defineModClient, useModGltf, useTerrainSampler } from '@vibelands/mod-sdk/client';
function PicnicLayer() {
const gltf = useModGltf('picnic-table.glb');
const getGroundHeight = useTerrainSampler();
const x = 8, z = -6;
return <primitive object={gltf.scene} position={[x, getGroundHeight(x, z), z]} name="picnic-table" />;
}
export default defineModClient({ WorldLayer: PicnicLayer });