MCPInstall

worldlabs

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 WLTAPIKEY environment variable.

Installs
830
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/playableintelligence/game-creator --skill worldlabs
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads worldlabs automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

World Labs — 3D World/Environment Generation

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.

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

Input Priority

Image-first — always prefer image input over text:

  1. Image mode (default) — if the user has a reference image, concept art, screenshot, or photo, use --mode image. This produces the most faithful results because the AI can match the exact visual style, layout, lighting, and mood.
  2. Text mode (fallback) — only use --mode text 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.

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

Tech Stack

ComponentTechnology
APIWorld Labs Marble API (https://api.worldlabs.ai/marble/v1)
AuthWLT-Api-Key header
Output: VisualGaussian Splat (.spz) — 100k, 500k, full resolution tiers
Output: PhysicsCollider mesh (.glb) — for collision detection
Output: SkyboxPanorama image (.jpg/.png)
Browser RendererSparkJS 2.x (@sparkjsdev/spark) — SparkRenderer + SplatMesh, Three.js compatible
CLI Scriptscripts/worldlabs-generate.mjs (zero dependencies)

Environment Variable

Before prompting the user, check if the key already exists: test -f .env && grep -q '^WORLDLABS_API_KEY=.' .env && echo "found" If found, export it with set -a; . .env; set +a and skip the prompt.

If not set, ask the user:

I'll generate a photorealistic 3D environment with World Labs. You can get a free API key:

  1. Sign up at https://platform.worldlabs.ai
  2. Go to API Keys
  3. Create a new key

Paste your key below like: WORLDLABS_API_KEY=your-key-here (It will be saved to .env and redacted from this conversation automatically.)

Or type "skip" to use basic geometry instead.

CLI Script Usage

# 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 list

Output 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 URLs

Integration with Three.js Games

Tested & working — see examples/worldlabs-arcade/ for a complete runnable demo.

Install SparkJS 2.0

npm install @sparkjsdev/spark@^2.0.0

Package: @sparkjsdev/spark — high-performance Gaussian Splat renderer for Three.js. Pin to ^2.0.0; the 0.x line has a different API (no SparkRenderer) and will not match the snippets below. Supports SPZ, PLY, SOGS, KSPLAT, SPLAT formats (auto-detected by extension).

Three.js peer version: Spark 2.0 declares three@^0.180.0 as its peer dependency. Three treats every 0.x minor as potentially breaking, so three@^0.181 or newer triggers an ERESOLVE peer-dep error on npm install. Pin three to ^0.180.0 in your project's package.json — this is what the threejs-3d template and the worldlabs-arcade example both use.

No bundler? Use the CDN importmap instead of npm:

<script type="importmap">{
  "imports": {
    "three": "https://cdnjs.cloudflare.com/ajax/libs/three.js/0.180.0/three.module.js",
    "@sparkjsdev/spark": "https://sparkjs.dev/releases/spark/2.0.0/spark.module.js"
  }
}</script>

WebGLRenderer config: Create it with antialias: false. Spark's splat shader provides its own anti-aliasing, and enabling MSAA on top wastes GPU for no visual gain:

const renderer = new THREE.WebGLRenderer({ antialias: false });

Constants.js — World Configuration

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 },
};

WorldLoader.js — Load Gaussian Splat + Collider

Spark 2.0 introduces SparkRenderer — a scene-level object that hooks into Three.js's render pipeline and controls splat quality, sorting, and LOD. You instantiate it once, add it to the scene, then add SplatMesh instances as normal Three.js objects. The WebGLRenderer's render() call handles everything transparently.

import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { SparkRenderer, SplatMesh } from '@sparkjsdev/spark';
import { WORLD } from '../core/Constants.js';

let _colliderMesh = null;
let _splatMesh = null;

export async function loadWorld(scene, renderer, camera) {
  // 0. SparkRenderer — quality + LOD controls for splat rendering.
  //    Must be added to the scene BEFORE any SplatMesh. One per scene.
  const spark = new SparkRenderer({
    renderer,
    maxPixelRadius: 512,   // bigger max → sharper close-up splats
    sortRadial: true,      // eliminates black-bar artifacts on camera rotation
    enableLod: true,       // auto-reduce splat count at distance
    lodSplatScale: 1.0,    // >1.0 = more splats (higher quality), <1.0 = fewer
  });
  scene.add(spark);

  const promises = [];

  // 1. Gaussian Splat via SparkJS — SplatMesh works like any Three.js object.
  //    `splat.initialized` is a Promise that resolves once the file is parsed
  //    and GPU buffers are uploaded.
  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);
      if (splat.initialized) await splat.initialized;
      _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; });
      _colliderMesh.updateMatrixWorld(true);   // needed before the first raycast
      scene.add(_colliderMesh);
    })());
  }

  // 3. Panorama as equirectangular skybox + environment lighting.
  //    NOTE: leave this OFF when using a World Labs splat scene — the panorama
  //    depicts the same environment as the splat and you get a "world inside a
  //    world" doubling effect. For IBL on Meshy characters, prefer
  //    `spark.renderEnvMap({ scene, worldCenter })` to light from the splat itself.
  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.environment = panoTex;   // lighting only — NOT scene.background
    })());
  }

  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

Once SparkRenderer is in the scene, splat rendering is transparent. SparkRenderer.autoUpdate defaults to true, so renderer.render(scene, camera) handles sort + draw for every SplatMesh in the scene — no extra .render() or .update() call needed. Use renderer.setAnimationLoop (Spark 2.0's recommended loop form; also WebXR-compatible).

import { loadWorld, getGroundHeight } from '../level/WorldLoader.js';

// In init():
await loadWorld(scene, renderer, camera);

// Render loop — standard Three.js, no extra splat pass:
renderer.setAnimationLoop((time) => {
  const delta = clock.getDelta();

  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);
});

Resolution Tiers

TierFileQualityUse Case
100k{slug}-100k.spzLowMobile, fast loading, previews
500k{slug}-500k.spzMediumDesktop games, good balance
full_res{slug}.spzHighHigh-end, hero environments

Choose based on target platform. The collider mesh (GLB) is the same regardless of splat resolution.

Spark 2.0 Quality Tuning

The default SparkRenderer({ renderer }) looks good for most scenes. When it doesn't, these are the four knobs that actually move the needle — pass them in the SparkRenderer constructor:

SymptomKnobDirection
Close-up splats look chunky / low-reslodSplatScaleRaise toward 2.0 (2× splats rendered)
Splats visibly pop in/out at distancelodRenderScaleRaise toward 2.0 to keep distant splats larger on screen
Black bars or tearing when camera rotatessortRadialKeep true (default). Set false only if you need strict Z-depth for compositing.
Low-end GPU / mobile perf is poorlodSplatScale + maxPixelRadiusDrop lodSplatScale to 0.5, maxPixelRadius to 256

IBL from the splat scene — Spark 2.0 adds spark.renderEnvMap({ scene, worldCenter }) which renders the splat scene into a THREE.Texture usable as scene.environment or MeshStandardMaterial.envMap. This is the proper way to make Meshy characters catch the lighting of the World Labs environment they stand in. See the SparkRenderer docs for full options.

Pipeline: World Labs + Meshy AI

For a complete 3D game, combine both:

  1. World Labs → Generate the environment (room, landscape, arena)
  2. Meshy AI → Generate characters and props (player, enemies, items)
  3. 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    │
└─────────────────────────────────────────────────┘

Reference Implementation

See examples/worldlabs-arcade/ — a complete, tested demo with:

  • World Labs Gaussian Splat environment (retro arcade)
  • Animated Soldier character (walk/run/idle)
  • OrbitControls third-person camera
  • Collider-mesh ground raycasting
  • Panorama skybox

Troubleshooting

Scene appears upside down (Y-flip)

Cause: World Labs SPZ files use Y-inverted coordinates compared to Three.js convention. Fix: Apply rotation.x = Math.PI to both the splat mesh and collider mesh. Then adjust position.z to compensate: position.z += (minZ + maxZ). Do NOT use scale.y = -1 on a parent group — SparkJS breaks with negative parent scale.

Raycast 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 (0, 1, 0) to hit the visual floor first. The floor is the lowest surface after the flip.

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 _colliderMesh.updateMatrixWorld(true) immediately after setting rotation and position, before any raycast operations.

Scene appears doubled / world inside a world

Cause: Using the World Labs panorama as scene.background 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 (scene.background = new THREE.Color(0x87CEEB)) or a custom skybox instead.

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 progress.status for "IN_PROGRESS" vs "COMPLETE". If stuck beyond 15 minutes, create a new generation request — don't retry the same operation.

Splat file loads but nothing renders (black scene)

Cause: In Spark 2.0 the SparkRenderer is what actually draws splats. Adding a SplatMesh to the scene without a SparkRenderer also in the scene produces no output. Fix: Instantiate new SparkRenderer({ renderer }) and scene.add(spark) before any SplatMesh is added. One SparkRenderer per scene is enough.

Wrong Spark API / methods don't exist

Cause: Installed 0.x instead of 2.x. The 0.x line predates SparkRenderer, LOD, and sortRadial. Fix: Pin to 2.0 or later: npm install @sparkjsdev/spark@^2.0.0. Check node_modules/@sparkjsdev/spark/package.json to confirm.

Splat pops in/out as camera moves

Cause: The LOD system is swapping tiers. Fine for large worlds, distracting in small ones. Fix: Raise lodRenderScale to keep distant splats visually larger, or set enableLod: false for scenes small enough to render at full detail everywhere.

Characters look unlit / don't match the splat environment

Cause: Meshy (or other GLB) characters use MeshStandardMaterial with no environment map, so they render flat against a photorealistic splat backdrop. Fix: Use spark.renderEnvMap({ scene, worldCenter }) to bake the splat scene into a THREE.Texture and assign it: scene.environment = envMap. Or assign per-material via spark.recurseSetEnvMap(character, envMap).

Media upload fails with 404

Cause: Using the wrong endpoint for media upload preparation. Fix: Use POST /media-assets:prepare_upload (NOT /media-assets). This returns a signed URL for PUT upload. The colon syntax is intentional — it's a custom action on the resource.

API returns 401 Unauthorized

Cause: Using the wrong authentication header format. Fix: Use WLT-Api-Key: <your-key> header, NOT Authorization: Bearer <your-key>. The World Labs API uses a custom header format.

Checklist

  • WORLDLABS_API_KEY environment variable is set
  • Ask user for reference image first (image-to-world is preferred)
  • Run scripts/worldlabs-generate.mjs to generate world (~3-8 min)
  • SPZ + collider GLB + panorama downloaded to public/assets/worlds/
  • @sparkjsdev/spark@^2.0.0 installed (not 0.x — different API)
  • WebGLRenderer created with antialias: false
  • WorldLoader.js created in src/level/ (SparkRenderer + SplatMesh + GLTFLoader)
  • SparkRenderer instantiated and added to the scene before any SplatMesh
  • await splat.initialized used before treating the splat as loaded
  • Constants.js updated with WORLD config (splatPath, colliderPath, panoPath)
  • Game.js calls loadWorld() in init, uses getGroundHeight() for player Y
  • Single renderer.render(scene, camera) handles both splats and meshes — no extra pass
  • 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; tune lodSplatScale if needed

FAQ

What does the worldlabs skill do?

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 WLTAPIKEY environment variable.

How do I install worldlabs?

Run npx skills add https://github.com/playableintelligence/game-creator --skill worldlabs in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/worldlabs/ (personal) or .claude/skills/worldlabs/ (project).

Which AI agents can use worldlabs?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is worldlabs safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains worldlabs?

It is published by playableintelligence in playableintelligence/game-creator. It has 830 recorded installs.

More from playableintelligence/game-creator

View all →

game-designer

playableintelligence/game-creator

Game UI/UX designer that analyzes and improves the visual polish, atmosphere, and player experience of browser games. Use when a game needs visual improvements, better backgrounds, particles, animations, screen transitions, juice/feel, or overall aesthetic upgrades.

Frontend & Design1.9K installs

game-assets

playableintelligence/game-creator

Game asset engineer that creates pixel art sprites, animated characters, and visual entities for browser games. Use when a game needs better character art, enemy sprites, item visuals, or any upgrade from basic geometric shapes to recognizable pixel art.

Media & Creative1.6K installs

design-game

playableintelligence/game-creator

Audit and improve the visual design, polish, and player experience of an existing game. Use when the user says "make my game look better", "improve the design", "add polish", "add juice", "add particles", "fix the UI", or "make it more visually appealing". Do NOT use for gameplay logic changes (use add-feature instead).

Media & Creative1.4K installs

phaser

playableintelligence/game-creator

Build 2D browser games with Phaser 3 using scene-based architecture and centralized state. Use when creating a new 2D game, adding 2D game features, working with Phaser, or building sprite-based web games.

Media & Creative1.4K installs

game-audio

playableintelligence/game-creator

Game audio engineer using Web Audio API for procedural music and sound effects in browser games. Zero dependencies. Use when adding music or SFX to a game.

Media & Creative1.3K installs

game-architecture

playableintelligence/game-creator

Game architecture patterns and best practices for browser games. Use when designing game systems, planning architecture, structuring a game project, or making architectural decisions about game code.

Popular media & creative skills

View category →

hyperframes

heygen-com/hyperframes

Read first to make, edit, animate or render any video with HyperFrames (HTML rendered to video), including launch films, explainers, music videos, charts, maps, vertical clips, title cards, captions and overlays. Holds the render contract, the work loop and what each kind of video needs.

Media & Creative1.2M installs

media-use

heygen-com/hyperframes

Get or make any media for a HyperFrames video - music, sound effects, images, icons, logos, voiceover, avatar and generated video, transcripts, cutouts, colour grades and looks - with HeyGen or free local tools, and mix placed audio. Also for footage that looks dark, flat or dull, or should be stylized or hidden.

Media & Creative1.1M installs

hyperframes-registry

heygen-com/hyperframes

About 450 ready-made HyperFrames blocks and components installed as editable source - transitions, overlays, titles, kinetic type, captions, charts, map overlays, 3D, camera moves. Use it before hand-building any named visual (CRT scanlines, glitch, film grain, a chart); also for hyperframes add and catalog.

Media & Creative1M installs

hyperframes-creative

heygen-com/hyperframes

Non-animation creative direction for HyperFrames videos. Use for design spec (frame.md / design.md) handling, palettes, typography, narration, beat planning, audio-reactive visuals, composition patterns, and brand / style decisions. For atomic motion patterns and scene blueprints, use hyperframes-animation.

Media & Creative916.5K installs

ai-video-generation

101-skills/superpowers

Generate AI videos with Google Veo, Seedance 2.0, HappyHorse, Wan, Grok and 40+ models via inference.sh CLI. Models: Veo 3.1, Veo 3, Seedance 2.0, HappyHorse 1.0, Wan 2.5, Grok Imagine Video, OmniHuman, Fabric, HunyuanVideo. Capabilities: text-to-video, image-to-video, reference-to-video, video editing, lipsync, avatar animation, video upscaling, foley sound. Use for: social media videos, marketing content, explainer videos, product demos, AI avatars. Triggers: video generation, ai video, text to video, image to video, veo, animate image, video from image, ai animation, video generator, generate video, t2v, i2v, ai video maker, create video with ai, runway alternative, pika alternative, sora alternative, kling alternative, seedance, happyhorse

Media & Creative881.5K installs

ai-image-generation

101-skills/superpowers

Generate AI images with GPT-Image-2, FLUX, Gemini, Grok, Seedream, Reve and 50+ models via inference.sh CLI. Models: GPT-Image-2, FLUX Dev LoRA, FLUX.2 Klein LoRA, Gemini 3 Pro Image, Grok Imagine, Seedream 4.5, Reve, ImagineArt. Capabilities: text-to-image, image-to-image, inpainting, LoRA, image editing, upscaling, text rendering. Use for: AI art, product mockups, concept art, social media graphics, marketing visuals, illustrations. Triggers: flux, image generation, ai image, text to image, stable diffusion, generate image, ai art, midjourney alternative, dall-e alternative, text2img, t2i, image generator, ai picture, create image with ai, generative ai, ai illustration, grok image, gemini image, gpt image, openai image, chatgpt image

Media & Creative880.3K installs