Loading...
Loading...
Generate photorealistic 3D worlds and environments with the World Labs Marble API — Gaussian Splat scenes from text prompts or reference images. Use when the user says "generate a 3D world", "create an environment", "make a 3D scene", or "use World Labs". Requires WLT_API_KEY environment variable.
npx skill4agent add opusgamelabs/game-creator worldlabs--mode image--mode textI can generate a photorealistic 3D environment for your game using World Labs. Do you have a reference image (photo, concept art, screenshot) for the environment?
- Yes → provide the file path or URL
- No → I'll generate from a text description instead
| Component | Technology |
|---|---|
| API | World Labs Marble API ( |
| Auth | |
| Output: Visual | Gaussian Splat ( |
| Output: Physics | Collider mesh ( |
| Output: Skybox | Panorama image ( |
| Browser Renderer | SparkJS ( |
| CLI Script | |
export WORLDLABS_API_KEY=<your-key># Text to 3D world
WORLDLABS_API_KEY=<key> node scripts/worldlabs-generate.mjs \
--mode text --prompt "a medieval tavern with wooden beams and a roaring fireplace" \
--output public/assets/worlds/ --slug tavern
# Image to 3D world (local file or URL)
WORLDLABS_API_KEY=<key> node scripts/worldlabs-generate.mjs \
--mode image --image ./reference-photo.jpg \
--output public/assets/worlds/ --slug my-world
# Check generation status
WORLDLABS_API_KEY=<key> node scripts/worldlabs-generate.mjs \
--mode status --operation-id <op-id>
# Download assets from existing world
WORLDLABS_API_KEY=<key> node scripts/worldlabs-generate.mjs \
--mode get --world-id <id> --output public/assets/worlds/ --slug my-world
# List your worlds
WORLDLABS_API_KEY=<key> node scripts/worldlabs-generate.mjs --mode listpublic/assets/worlds/
tavern.spz # Gaussian Splat (full resolution)
tavern-500k.spz # Gaussian Splat (500k, medium quality)
tavern-100k.spz # Gaussian Splat (100k, lightweight/mobile)
tavern-collider.glb # Collider mesh for physics (GLB)
tavern-pano.jpg # Panorama image (skybox)
tavern.meta.json # Metadata: world ID, prompt, timestamps, asset URLsexamples/worldlabs-arcade/npm install @sparkjsdev/spark@sparkjsdev/sparkexport const WORLD = {
splatPath: 'assets/worlds/tavern-500k.spz', // 500k is a good desktop default
colliderPath: 'assets/worlds/tavern-collider.glb',
panoPath: 'assets/worlds/tavern-pano.png',
scale: 1,
position: { x: 0, y: 0, z: 0 },
};import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { SplatMesh } from '@sparkjsdev/spark';
import { WORLD } from '../core/Constants.js';
let _colliderMesh = null;
let _splatMesh = null;
export async function loadWorld(scene, renderer, camera) {
const promises = [];
// 1. Gaussian Splat via SparkJS — SplatMesh works like any Three.js object
if (WORLD.splatPath) {
promises.push((async () => {
const splat = new SplatMesh({ url: WORLD.splatPath });
splat.scale.setScalar(WORLD.scale);
splat.position.set(WORLD.position.x, WORLD.position.y, WORLD.position.z);
scene.add(splat);
_splatMesh = splat;
})());
}
// 2. Collider mesh (GLB) — invisible, for physics raycasting only
if (WORLD.colliderPath) {
promises.push((async () => {
const loader = new GLTFLoader();
const gltf = await loader.loadAsync(WORLD.colliderPath);
_colliderMesh = gltf.scene;
_colliderMesh.visible = false;
_colliderMesh.scale.setScalar(WORLD.scale);
_colliderMesh.position.set(WORLD.position.x, WORLD.position.y, WORLD.position.z);
_colliderMesh.traverse(c => { if (c.isMesh) c.material.side = THREE.DoubleSide; });
scene.add(_colliderMesh);
})());
}
// 3. Panorama as equirectangular skybox + environment lighting
if (WORLD.panoPath) {
promises.push((async () => {
const texLoader = new THREE.TextureLoader();
const panoTex = await texLoader.loadAsync(WORLD.panoPath);
panoTex.mapping = THREE.EquirectangularReflectionMapping;
panoTex.colorSpace = THREE.SRGBColorSpace;
scene.background = panoTex;
scene.environment = panoTex;
})());
}
await Promise.all(promises);
return { splat: _splatMesh, collider: _colliderMesh };
}
// Raycast down to find ground height on collider mesh
const _raycaster = new THREE.Raycaster();
const _downDir = new THREE.Vector3(0, -1, 0);
const _rayOrigin = new THREE.Vector3();
export function getGroundHeight(x, z, fallback = 0) {
if (!_colliderMesh) return fallback;
_rayOrigin.set(x, 50, z);
_raycaster.set(_rayOrigin, _downDir);
const hits = _raycaster.intersectObject(_colliderMesh, true);
return hits.length > 0 ? hits[0].point.y : fallback;
}
export function getCollider() { return _colliderMesh; }scene.add(splat)renderer.render(scene, camera)import { loadWorld, getGroundHeight } from '../level/WorldLoader.js';
// In init():
await loadWorld(scene, renderer, camera);
// In the render loop — standard Three.js, no extra splat pass:
function animate() {
requestAnimationFrame(animate);
player.update(delta, input, azimuth);
// Snap player Y to collider ground
const groundY = getGroundHeight(player.mesh.position.x, player.mesh.position.z, 0);
player.mesh.position.y = groundY;
// Single render call handles both meshes AND splats
renderer.render(scene, camera);
}| Tier | File | Quality | Use Case |
|---|---|---|---|
| | Low | Mobile, fast loading, previews |
| | Medium | Desktop games, good balance |
| | High | High-end, hero environments |
┌─────────────────────────────────────────────────┐
│ Complete 3D Scene │
├─────────────────────────────────────────────────┤
│ World Labs (environment) │
│ └─ Gaussian Splat (visual) │
│ └─ Collider mesh (physics) │
│ └─ Panorama (skybox) │
│ │
│ Meshy AI (entities) │
│ └─ Player character (rigged, animated GLB) │
│ └─ Enemies (rigged, animated GLB) │
│ └─ Props/items (static GLB) │
│ │
│ Three.js (engine) │
│ └─ SparkJS renders splats │
│ └─ GLTFLoader renders characters/props │
│ └─ Raycaster uses collider for ground/walls │
└─────────────────────────────────────────────────┘examples/worldlabs-arcade/rotation.x = Math.PIposition.z += (minZ + maxZ)scale.y = -1(0, 1, 0)_colliderMesh.updateMatrixWorld(true)scene.backgroundscene.background = new THREE.Color(0x87CEEB)progress.status"IN_PROGRESS""COMPLETE"@sparkjsdev/spark@worldlabs/sparknpm install @sparkjsdev/sparkSplatMeshPOST /media-assets:prepare_upload/media-assetsWLT-Api-Key: <your-key>Authorization: Bearer <your-key>WORLDLABS_API_KEYscripts/worldlabs-generate.mjspublic/assets/worlds/@sparkjsdev/sparknpm install @sparkjsdev/sparkWorldLoader.jssrc/level/Constants.jsWORLDGame.jsloadWorld()getGroundHeight()renderer.render(scene, camera)