worldlabs
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWorld Labs — 3D World/Environment Generation
World Labs — 3D世界/环境生成
Generate photorealistic 3D environments from text prompts or images using the World Labs Marble API. Outputs Gaussian Splat scenes (SPZ) rendered via SparkJS in Three.js, plus collider meshes (GLB) for physics.
借助World Labs Marble API,通过文本提示词或图像生成照片级真实感的3D环境。输出可通过Three.js中的SparkJS渲染的Gaussian Splat场景(SPZ),以及用于物理碰撞的碰撞器网格(GLB)。
When to Use
使用场景
- Environment/level generation — create entire 3D worlds (rooms, landscapes, buildings) from reference images or text
- Complementary to Meshy AI — Meshy generates individual models/characters; World Labs generates the environments they exist in
- Photorealistic scenes — Gaussian Splats produce photorealistic quality vs mesh-based environments
- 环境/关卡生成——通过参考图像或文本创建完整的3D世界(房间、景观、建筑)
- 与Meshy AI互补——Meshy生成单个模型/角色;World Labs生成它们所处的环境
- 照片级真实感场景——与基于网格的环境相比,Gaussian Splat可生成照片级真实感的画面
Input Priority
输入优先级
Image-first — always prefer image input over text:
- Image mode (default) — if the user has a reference image, concept art, screenshot, or photo, use . This produces the most faithful results because the AI can match the exact visual style, layout, lighting, and mood.
--mode image - Text mode (fallback) — only use when no reference image is available. The API auto-expands short prompts into rich scene descriptions, but results are less predictable than image-driven generation.
--mode text
When the game-creator pipeline runs, ask the user for a reference image first:
I 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
图像优先——始终优先使用图像输入而非文本:
- 图像模式(默认)——如果用户有参考图像、概念图、截图或照片,使用。这种方式能生成最贴合需求的结果,因为AI可以匹配精确的视觉风格、布局、光线和氛围。
--mode image - 文本模式(备选)——仅在没有参考图像时使用。API会将简短提示词自动扩展为丰富的场景描述,但结果的可预测性低于图像驱动的生成。
--mode text
当游戏创建流程启动时,首先询问用户是否有参考图像:
我可以使用World Labs为你的游戏生成照片级真实感的3D环境。 你是否有该环境的参考图像(照片、概念图、截图)?
- 是 → 提供文件路径或URL
- 否 → 我将通过文本描述生成
Tech Stack
技术栈
| 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 | |
| 组件 | 技术 |
|---|---|
| API | World Labs Marble API ( |
| 认证 | |
| 输出:视觉内容 | Gaussian Splat( |
| 输出:物理碰撞 | 碰撞器网格( |
| 输出:天空盒 | 全景图像( |
| 浏览器渲染器 | SparkJS ( |
| CLI脚本 | |
Environment Variable
环境变量
bash
export WORLDLABS_API_KEY=<your-key>Get an API key at: https://platform.worldlabs.ai/api-keys
bash
export WORLDLABS_API_KEY=<your-key>CLI Script Usage
CLI脚本使用方法
bash
undefinedbash
undefinedText to 3D world
文本转3D世界
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
--mode text --prompt "a medieval tavern with wooden beams and a roaring fireplace"
--output public/assets/worlds/ --slug tavern
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
--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)
图像转3D世界(本地文件或URL)
WORLDLABS_API_KEY=<key> node scripts/worldlabs-generate.mjs
--mode image --image ./reference-photo.jpg
--output public/assets/worlds/ --slug my-world
--mode image --image ./reference-photo.jpg
--output public/assets/worlds/ --slug my-world
WORLDLABS_API_KEY=<key> node scripts/worldlabs-generate.mjs
--mode image --image ./reference-photo.jpg
--output public/assets/worlds/ --slug my-world
--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>
--mode status --operation-id <op-id>
WORLDLABS_API_KEY=<key> node scripts/worldlabs-generate.mjs
--mode status --operation-id <op-id>
--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
--mode get --world-id <id> --output public/assets/worlds/ --slug my-world
WORLDLABS_API_KEY=<key> node scripts/worldlabs-generate.mjs
--mode get --world-id <id> --output public/assets/worlds/ --slug my-world
--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 list
undefinedWORLDLABS_API_KEY=<key> node scripts/worldlabs-generate.mjs --mode list
undefinedOutput Files
输出文件
public/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 URLspublic/assets/worlds/
tavern.spz # Gaussian Splat(全分辨率)
tavern-500k.spz # Gaussian Splat(500k,中等质量)
tavern-100k.spz # Gaussian Splat(100k,轻量/移动端适配)
tavern-collider.glb # 物理碰撞器网格(GLB)
tavern-pano.jpg # 全景图像(天空盒)
tavern.meta.json # 元数据:世界ID、提示词、时间戳、资源URLIntegration with Three.js Games
与Three.js游戏集成
Tested & working — see for a complete runnable demo.
examples/worldlabs-arcade/已测试并可正常使用——查看获取完整可运行的示例。
examples/worldlabs-arcade/Install SparkJS
安装SparkJS
bash
npm install @sparkjsdev/sparkPackage: — high-performance Gaussian Splat renderer for Three.js. Supports SPZ, PLY, SOGS, KSPLAT, SPLAT formats.
@sparkjsdev/sparkbash
npm install @sparkjsdev/spark包名:——适用于Three.js的高性能Gaussian Splat渲染器。支持SPZ、PLY、SOGS、KSPLAT、SPLAT格式。
@sparkjsdev/sparkConstants.js — World Configuration
Constants.js — 世界配置
js
export 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 },
};js
export const WORLD = {
splatPath: 'assets/worlds/tavern-500k.spz', // 500k是桌面端的理想默认值
colliderPath: 'assets/worlds/tavern-collider.glb',
panoPath: 'assets/worlds/tavern-pano.png',
scale: 1,
position: { x: 0, y: 0, z: 0 },
};WorldLoader.js — Load Gaussian Splat + Collider
WorldLoader.js — 加载Gaussian Splat + 碰撞器
js
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; }js
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. 借助SparkJS加载Gaussian Splat — SplatMesh的使用方式与普通Three.js对象一致
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. 碰撞器网格(GLB)——不可见,仅用于物理射线检测
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. 将全景图作为等距天空盒 + 环境光
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 };
}
// 向下射线检测以获取碰撞器网格上的地面高度
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; }Game.js — Render Loop Integration
Game.js — 渲染循环集成
SplatMesh renders as part of the normal Three.js scene — no separate render pass needed. Just and call as usual.
scene.add(splat)renderer.render(scene, camera)js
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);
}SplatMesh作为普通Three.js场景的一部分进行渲染——无需单独的渲染通道。只需执行,然后像往常一样调用即可。
scene.add(splat)renderer.render(scene, camera)js
import { loadWorld, getGroundHeight } from '../level/WorldLoader.js';
// 在init()中:
await loadWorld(scene, renderer, camera);
// 在渲染循环中——标准Three.js流程,无需额外的splat渲染步骤:
function animate() {
requestAnimationFrame(animate);
player.update(delta, input, azimuth);
// 将玩家Y轴位置对齐到碰撞器地面
const groundY = getGroundHeight(player.mesh.position.x, player.mesh.position.z, 0);
player.mesh.position.y = groundY;
// 单次渲染调用即可处理网格和splat
renderer.render(scene, camera);
}Resolution Tiers
分辨率层级
| Tier | File | Quality | Use Case |
|---|---|---|---|
| | Low | Mobile, fast loading, previews |
| | Medium | Desktop games, good balance |
| | High | High-end, hero environments |
Choose based on target platform. The collider mesh (GLB) is the same regardless of splat resolution.
| 层级 | 文件 | 质量 | 使用场景 |
|---|---|---|---|
| | 低 | 移动端、快速加载、预览 |
| | 中 | 桌面端游戏,平衡质量与性能 |
| | 高 | 高端设备、核心展示环境 |
根据目标平台选择合适的层级。无论splat分辨率如何,碰撞器网格(GLB)都是相同的。
Pipeline: World Labs + Meshy AI
工作流:World Labs + Meshy AI
For a complete 3D game, combine both:
- World Labs → Generate the environment (room, landscape, arena)
- Meshy AI → Generate characters and props (player, enemies, items)
- Integrate → Characters walk on World Labs collider mesh, rendered inside the Gaussian Splat scene
┌─────────────────────────────────────────────────┐
│ 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 │
└─────────────────────────────────────────────────┘构建完整3D游戏时,可结合两者的优势:
- World Labs → 生成环境(房间、景观、竞技场)
- Meshy AI → 生成角色和道具(玩家、敌人、物品)
- 集成 → 角色在World Labs的碰撞器网格上行走,在Gaussian Splat场景中渲染
┌─────────────────────────────────────────────────┐
│ 完整3D场景 │
├─────────────────────────────────────────────────┤
│ World Labs(环境) │
│ └─ Gaussian Splat(视觉内容) │
│ └─ 碰撞器网格(物理碰撞) │
│ └─ 全景图(天空盒) │
│ │
│ Meshy AI(实体) │
│ └─ 玩家角色(带骨骼绑定、动画的GLB) │
│ └─ 敌人(带骨骼绑定、动画的GLB) │
│ └─ 道具/物品(静态GLB) │
│ │
│ Three.js(引擎) │
│ └─ SparkJS渲染splat内容 │
│ └─ GLTFLoader渲染角色/道具 │
│ └─ Raycaster使用碰撞器检测地面/墙壁 │
└─────────────────────────────────────────────────┘Reference Implementation
参考实现
See — a complete, tested demo with:
examples/worldlabs-arcade/- World Labs Gaussian Splat environment (retro arcade)
- Animated Soldier character (walk/run/idle)
- OrbitControls third-person camera
- Collider-mesh ground raycasting
- Panorama skybox
查看——完整的已测试示例,包含:
examples/worldlabs-arcade/- World Labs Gaussian Splat环境(复古街机风格)
- 带动画的士兵角色(行走/奔跑/ idle)
- OrbitControls第三人称相机
- 碰撞器网格地面射线检测
- 全景天空盒
Troubleshooting
故障排除
Scene appears upside down (Y-flip)
场景显示颠倒(Y轴翻转)
Cause: World Labs SPZ files use Y-inverted coordinates compared to Three.js convention.
Fix: Apply to both the splat mesh and collider mesh. Then adjust position.z to compensate: . Do NOT use on a parent group — SparkJS breaks with negative parent scale.
rotation.x = Math.PIposition.z += (minZ + maxZ)scale.y = -1原因:World Labs的SPZ文件使用的Y轴坐标与Three.js的惯例相反。
解决方法:对splat网格和碰撞器网格应用。然后调整position.z进行补偿:。不要对父级组使用——SparkJS在父级缩放为负数时会出现异常。
rotation.x = Math.PIposition.z += (minZ + maxZ)scale.y = -1Raycast hits ceiling instead of floor
射线检测击中天花板而非地面
Cause: After Y-flip, the coordinate system is inverted. Downward raycasts hit what was originally the floor (now the ceiling in flipped space).
Fix: Raycast UPWARD from Y=-50 with direction to hit the visual floor first. The floor is the lowest surface after the flip.
(0, 1, 0)原因:Y轴翻转后,坐标系被反转。向下的射线检测会击中原本的地面(在翻转后的空间中变为天花板)。
解决方法:从Y=-50的位置向上射线检测,方向为,这样会首先击中视觉上的地面。翻转后的地面是空间中最低的表面。
(0, 1, 0)Collider mesh raycasts return no hits
碰撞器网格射线检测无结果
Cause: The collider mesh's world matrix hasn't been updated after setting rotation/position, especially before the first render frame.
Fix: Call immediately after setting rotation and position, before any raycast operations.
_colliderMesh.updateMatrixWorld(true)原因:设置旋转/位置后,碰撞器网格的世界矩阵未更新,尤其是在第一帧渲染前。
解决方法:在设置旋转和位置后,立即调用,再执行任何射线检测操作。
_colliderMesh.updateMatrixWorld(true)Scene appears doubled / world inside a world
场景显示重复 / 世界套娃
Cause: Using the World Labs panorama as shows the same environment as a giant sphere surrounding the 3D scene.
Fix: Don't use the panorama as scene background. Use a solid color () or a custom skybox instead.
scene.backgroundscene.background = new THREE.Color(0x87CEEB)原因:将World Labs的全景图设置为后,会在3D场景周围显示一个巨大的球体,内容与当前环境相同。
解决方法:不要将全景图作为场景背景。使用纯色()或自定义天空盒代替。
scene.backgroundscene.background = new THREE.Color(0x87CEEB)Generation times out or takes too long
生成超时或耗时过长
Cause: World Labs generation typically takes 3-8 minutes. Complex scenes or high server load can extend this.
Fix: Poll the operation status endpoint every 10-15 seconds. Check for vs . If stuck beyond 15 minutes, create a new generation request — don't retry the same operation.
progress.status"IN_PROGRESS""COMPLETE"原因:World Labs的生成流程通常需要3-8分钟。复杂场景或服务器负载过高可能会延长耗时。
解决方法:每10-15秒轮询一次操作状态接口。检查是还是。如果超过15分钟仍未完成,创建新的生成请求——不要重试同一个操作。
progress.status"IN_PROGRESS""COMPLETE"Cannot find SparkJS package
找不到SparkJS包
Cause: The package name is , not or other variations.
Fix: Install with . Import from this package. It integrates directly into the Three.js scene — no separate render pass needed.
@sparkjsdev/spark@worldlabs/sparknpm install @sparkjsdev/sparkSplatMesh原因:包名称是,而非或其他变体。
解决方法:使用安装。从该包导入。它可直接集成到Three.js场景中——无需单独的渲染通道。
@sparkjsdev/spark@worldlabs/sparknpm install @sparkjsdev/sparkSplatMeshMedia upload fails with 404
媒体上传失败,返回404
Cause: Using the wrong endpoint for media upload preparation.
Fix: Use (NOT ). This returns a signed URL for PUT upload. The colon syntax is intentional — it's a custom action on the resource.
POST /media-assets:prepare_upload/media-assets原因:使用了错误的媒体上传准备接口。
解决方法:使用(而非)。该接口会返回一个用于PUT上传的签名URL。冒号语法是有意设计的——它是资源的自定义操作。
POST /media-assets:prepare_upload/media-assetsAPI returns 401 Unauthorized
API返回401未授权
Cause: Using the wrong authentication header format.
Fix: Use header, NOT . The World Labs API uses a custom header format.
WLT-Api-Key: <your-key>Authorization: Bearer <your-key>原因:使用了错误的认证请求头格式。
解决方法:使用请求头,而非。World Labs API使用自定义的请求头格式。
WLT-Api-Key: <your-key>Authorization: Bearer <your-key>Checklist
检查清单
- environment variable is set
WORLDLABS_API_KEY - Ask user for reference image first (image-to-world is preferred)
- Run to generate world (~3-8 min)
scripts/worldlabs-generate.mjs - SPZ + collider GLB + panorama downloaded to
public/assets/worlds/ - installed (
@sparkjsdev/spark)npm install @sparkjsdev/spark - created in
WorldLoader.js(SplatMesh + GLTFLoader + TextureLoader)src/level/ - updated with
Constants.jsconfig (splatPath, colliderPath, panoPath)WORLD - calls
Game.jsin init, usesloadWorld()for player YgetGroundHeight() - Single handles both splats and meshes — no extra pass
renderer.render(scene, camera) - Physics uses invisible collider mesh for ground/wall raycasting
- Test: character walks on collider surface, splat renders around them
- Performance: use 500k SPZ for desktop, 100k for mobile
- 已设置环境变量
WORLDLABS_API_KEY - 首先询问用户是否有参考图像(优先使用图像转世界功能)
- 运行生成世界(耗时约3-8分钟)
scripts/worldlabs-generate.mjs - SPZ + 碰撞器GLB + 全景图已下载到
public/assets/worlds/ - 已安装(
@sparkjsdev/spark)npm install @sparkjsdev/spark - 在中创建了
src/level/(包含SplatMesh + GLTFLoader + TextureLoader)WorldLoader.js - 已更新中的
Constants.js配置(splatPath、colliderPath、panoPath)WORLD - 在初始化时调用
Game.js,使用loadWorld()设置玩家Y轴位置getGroundHeight() - 单次调用即可处理splat和网格——无需额外渲染通道
renderer.render(scene, camera) - 物理系统使用不可见的碰撞器网格进行地面/墙壁射线检测
- 测试:角色可在碰撞器表面行走,splat内容在周围正常渲染
- 性能优化:桌面端使用500k SPZ,移动端使用100k SPZ