# AI agent skill
Source: https://docs.worldlabs.ai/api/agent-skill
Install the World Labs API skill for coding agents
## Install the skill
The World Labs API skill gives coding agents API guidance and an OpenAPI
snapshot for client generation, world generation, media asset uploads, and
operation polling.
```bash theme={null}
npx skills add worldlabsai/marble-developer-api-skill --skill marble-developer-api
```
Install it globally if you want agents to use it across projects:
```bash theme={null}
npx skills add worldlabsai/marble-developer-api-skill --skill marble-developer-api --global
```
## Verify the install
List the skills exposed by the public mirror:
```bash theme={null}
npx skills add worldlabsai/marble-developer-api-skill --list
```
You should see `marble-developer-api` in the output.
## Use the skill
Ask your coding agent to invoke the skill by name:
```text theme={null}
Use the marble-developer-api skill to build a TypeScript client for World API v1.
```
```text theme={null}
Use the marble-developer-api skill to upload an image, generate a world, and poll the operation.
```
Never paste a real API key into prompts, logs, screenshots, or committed files.
The skill source is mirrored at
[worldlabsai/marble-developer-api-skill](https://github.com/worldlabsai/marble-developer-api-skill).
# Errors and troubleshooting
Source: https://docs.worldlabs.ai/api/errors
How to read World API error responses and fix the most common failures, including 400 Bad request
## Error responses
The World API uses standard HTTP status codes. Most errors return a JSON body
with a `detail` field that describes the problem:
```json theme={null}
{
"detail": "Failed to download asset from https://example.com/image.jpg. Please check the URL and try again."
}
```
Schema validation errors (`422`) return `detail` as an array, with one entry per
invalid field:
```json theme={null}
{
"detail": [
{
"loc": ["body", "world_prompt", "type"],
"msg": "Field required",
"type": "missing"
}
]
}
```
Some requests are rejected before they reach validation and come back with a
generic message and no `detail` field, such as `Bad request`. The most common
cause is a body that isn't valid JSON — see
[Request body is not valid JSON](#request-body-is-not-valid-json).
Many responses include a `request_id`. It's a trace identifier for the World
Labs team, not something you can look up yourself. Keep it and include it (with
your endpoint and request body, API key redacted) when you contact
[support@worldlabs.ai](mailto:support@worldlabs.ai).
## Status codes
| Status | Meaning | What to do |
| --------------------------- | ----------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400` Bad request | The request was invalid, or the prompt/input violated content policy. | Read `detail` when present. See [common causes](#common-400-causes) below. |
| `402` Payment required | The account has insufficient API credits. | Add credits or enable auto-refill on the [billing page](https://platform.worldlabs.ai/billing). API credits are separate from Marble app credits — see the [FAQ](/api/faq). |
| `404` Not found | The world, operation, or media asset doesn't exist or isn't owned by your API user. | Verify the ID and that it was created with the same API key. |
| `422` Unprocessable entity | The body didn't match the request schema. | Read the field path in each `detail[]` entry and fix that field. |
| `429` Too many requests | You exceeded a rate limit. | Back off and retry. See [Rate limits](/api/rate-limits). |
| `500` Internal server error | The request couldn't be processed. | Retry after a short delay; contact support if it persists. |
## Finding the specific reason
Where the failure reason lives depends on how the call fails:
* **Immediate HTTP errors** (the `worlds:generate` call itself returns `4xx`):
read the status code and the `detail` field, then apply the fixes below.
* **Generation failures** happen later. A `worlds:generate` call that returns
`200` with an `operation_id` can still fail during generation. Poll
[`GET /marble/v1/operations/{operation_id}`](/api/reference/operations/get);
when `done` is `true` with a non-null `error`, read `error.code` and
`error.message`.
## Common 400 causes
### Request body is not valid JSON
A frequent symptom is a generic `Bad request` with no `detail`, even though the
same payload works with `curl`. Some HTTP clients send a language-native object
or map through its default string conversion instead of serializing it to JSON.
That produces a body like this:
```text theme={null}
{model=marble-1.0, permission={allow_id_access=false, public=false}, world_prompt={...}}
```
That isn't JSON — it has unquoted keys, `=` instead of `:`, and no string
quoting, so it's rejected before it reaches the API. Valid JSON looks like
`{"model":"marble-1.0",...}`.
Fix: serialize the payload to a JSON string before sending, and set
`Content-Type: application/json`. Log the exact bytes you pass to your HTTP
client to confirm they're JSON.
### Image or video URL can't be fetched
When you use `source: "uri"`, World Labs fetches the URL from its servers with no
cookies, authentication, or referer. Hotlink-protected or login-gated hosts
(some CDNs and image-search result links) block these server-side fetches, which
returns a `400`.
Test the URL from a fresh environment — for example `curl` with no cookies or
referer. If it doesn't return the raw image or video bytes, the API can't use it
either. Use one of these instead:
* Upload the file as a media asset, then reference its `media_asset_id`. See
[Prepare a media asset upload](/api/reference/media-assets/prepare-upload) and
the [Quickstart upload flow](/api#image-input).
* Inline a small file with `source: "data_base64"`.
### Content policy
A prompt or input image or video can be rejected for content-policy reasons,
also as a `400`. Adjust the input and retry.
### Invalid model
`model` must be one of the supported values. See [Models](/api/models) for the
current list.
# Tools & examples
Source: https://docs.worldlabs.ai/api/examples
End-to-end examples for learning how to use the World API. These projects are intended for experimentation, not production use.
## API examples (Node & Python)
Minimal scripts and a simple web app for generating worlds using the raw API.
[View on GitHub](https://github.com/worldlabsai/worldlabs-api-examples)
## Client & splat utilities (Python)
Python client and utilities for saving/loading splats and rendering out videos.
[View on GitHub](https://github.com/worldlabsai/worldlabs-api-python)
## Spark
Render worlds on the web with SparkJS.
SparkJS is a high-performance 3D Gaussian splatting renderer built on top of THREE.js.
It is the recommended way to render World Labs splat assets in the browser.
Spark supports:
* Fast splat rendering on desktop and mobile
* Integration with other THREE.js meshes
* SPZ, PLY, SOGS, KSPLAT, and SPLAT formats
* Dynamic and procedural splat effects
[Explore SparkJS](https://sparkjs.dev/)
# Frequently asked questions
Source: https://docs.worldlabs.ai/api/faq
Common questions and answers about the World API
### Why does `worlds:generate` return `400 Bad request`?
A `400 Bad request` means the request was rejected before generation started. The response body is generic and includes a `request_id`, but not the exact cause. Other status codes narrow things down: `401` is an authentication problem, `402` is insufficient API credits, `422` is a schema validation error, and `429` is a rate limit.
The most common cause is a request body that is not valid JSON. This is the usual reason a request that works in `curl` fails from another HTTP client. Some libraries send an object's string form instead of JSON. For example, a Java `Map.toString()` produces `key=value` pairs with unquoted keys:
```text theme={null}
# Rejected: a stringified map, not JSON
{model=marble-1.0, world_prompt={type=text, text_prompt=A sunlit atrium}}
# Accepted: serialized JSON
{"model":"marble-1.0","world_prompt":{"type":"text","text_prompt":"A sunlit atrium"}}
```
Serialize the payload with a JSON library (Jackson, Gson, `encoding/json`, and similar) before sending it, and set `Content-Type: application/json`. Setting that header while sending non-JSON bytes still fails. To confirm, log the exact bytes you send and compare them against a working `curl` payload.
Other causes to check:
* An input URL that World Labs cannot fetch. For an `image_prompt` or `video_prompt` with `source: "uri"`, the URL must be reachable with no cookies, referer, or auth. Hosts that block hotlinking or use expiring links fail. Upload the file as a [media asset](/api/reference/media-assets/prepare-upload) or inline it with `source: "data_base64"` instead.
* Input that was rejected by content policy.
* An invalid `model` value. Use one of `marble-1.0-draft`, `marble-1.0`, `marble-1.1`, or `marble-1.1-plus`.
* A field constraint, such as `display_name` over 64 characters or more than 10 `tags`.
Keep the `request_id` from the response so support can trace the exact failure.
### Can I retrieve PLY files from the API?
Yes. Use [Export a world](/api/reference/worlds/export) to convert a generated world's SPZ splats into a cached `.ply` download:
```bash theme={null}
curl -X POST "https://api.worldlabs.ai/marble/v1/worlds/:export" \
-H "WLT-Api-Key: $WLT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"asset_type":"splats","format":"ply","resolution":"full_res"}'
```
The response is an operation. PLY conversions usually return with `done: true` and a signed download URL in `response.url`. You can also export a high-quality mesh with `{"asset_type":"mesh","format":"glb"}`; mesh exports are asynchronous, so poll the returned operation until it completes.
### How do I convert a generated world to real-world (metric) scale?
Generated splat assets are exported in arbitrary model units, not meters. The `semantics_metadata` object on the world response tells you how to convert to a metric, ground-aligned frame:
* `metric_scale_factor`: multiply every XYZ coordinate (and isotropic scale) by this to get meters.
* `ground_plane_offset`: after scaling, subtract this from the Y coordinate to place the ground plane at y=0.
```python theme={null}
def to_metric(xyz, semantics):
scaled = xyz * semantics.metric_scale_factor
scaled[..., 1] -= semantics.ground_plane_offset
return scaled
```
For Gaussian splat sizes, renderer coordinate systems, and log-scale `scale_0`
fields, see [Rendering Marble SPZ files in third-party engines](/api/rendering-spz).
### How does API billing work?
Billing for the World API is separate from billing for the Marble web app.
* Credits purchased for the Marble app cannot be used with the API
* API usage requires credits purchased through the World Labs Platform
If you plan to use the API, make sure you purchase credits on the World Labs Platform, NOT in the Marble app.
A Marble subscription — including a Free plan — says nothing about your API billing. The two are separate accounts even when you sign in with the same email, so being on the Marble Free plan does not mean your API usage is free.
### Why was I charged when I have not used the API recently?
Almost certainly this is an overage charge, and it covers an earlier billing period.
API credits are prepaid: you buy them up front and requests draw down that balance, so normal usage produces no charge at all. But requests are admitted based on a low-balance threshold rather than a per-request cost estimate, so a request that starts while your balance is low can cost more than you have left and take your balance below zero.
That uncovered usage — your overage — is the only part that gets invoiced, and it settles at the end of the monthly billing period rather than at the time of the request. So the charge can arrive weeks after your last request: a charge dated early August covers overage from July. The credits are converted to USD at your account's credit rate (\$1.00 USD per 1,250 credits on standard pay-as-you-go accounts).
Note that having auto-refill disabled does not prevent this. Auto-refill only controls whether your balance is topped up automatically; it does not cap usage.
To reconcile a charge, compare the billing period on the invoice against your usage events for that period on the [usage page](https://platform.worldlabs.ai/usage). See [Pricing](/api/pricing#overage) for details.
### How do I get a panorama image from my world generation?
Every world generation includes a panorama image in the response, accessible via `assets.imagery.pano_url`. This panorama is automatically generated as part of the world creation process.
The panorama URL will be available in the response at `operation.response.assets.imagery.pano_url` or when you fetch the world via `GET /marble/v1/worlds/{world_id}`.
### What is the difference between `marble-1.0`, `marble-1.1`, and `marble-1.1-plus`?
World Labs currently offers three API model variants for world generation:
* `marble-1.0` provides standard world generation at a fixed cost.
* `marble-1.1` provides newer standard world generation at a fixed cost.
* `marble-1.1-plus` provides the most expansive generation path and may add 0-1,500 variable world generation cost for larger worlds, determined automatically by the system during inference.
For the latest pricing details, see the [API pricing page](/api/pricing).
### Where can I read more about World Labs policies?
Please view our [Terms of Service](/terms-of-service) and [Privacy Policy](/privacy-policy) for details.
# Quickstart
Source: https://docs.worldlabs.ai/api/index
Learn how to use the World API
## Quickstart
Create API keys, manage billing, and check account settings.
See request patterns for text, image, pano, multi-image, and video inputs.
Sign in to the [World Labs Platform](https://platform.worldlabs.ai) with your Marble account.
If you don't have a Marble account, you'll be prompted to create one.
Visit the [billing page](https://platform.worldlabs.ai/billing).
Add a payment method to your account and then purchase some credits to get started.
Generate an API key from the [API keys page](https://platform.worldlabs.ai/api-keys).
Save your API key in a secure location and never share it with anyone.
To verify your development setup is working, we recommend creating a world from only a text prompt.
You can also create a world from an image, multiple images of the same scene, or a video.
This example uses `marble-1.1`, which corresponds to Marble 1.1.
For the largest worlds, use `marble-1.1-plus`. Marble 1.1 Plus uses more credits to create a bigger world when prompted for outdoor or larger indoor spaces.
Make a `POST` request to the [`/marble/v1/worlds:generate`](/api/reference/worlds/generate) endpoint.
```bash Request theme={null}
curl -X POST 'https://api.worldlabs.ai/marble/v1/worlds:generate' \
-H 'Content-Type: application/json' \
-H 'WLT-Api-Key: YOUR_API_KEY' \
-d '{
"display_name": "Mystical Forest",
"model": "marble-1.1",
"world_prompt": {
"type": "text",
"text_prompt": "A mystical forest with glowing mushrooms"
}
}'
```
```javascript Request theme={null}
const response = await fetch('https://api.worldlabs.ai/marble/v1/worlds:generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'WLT-Api-Key': 'YOUR_API_KEY'
},
body: JSON.stringify({
display_name: 'Mystical Forest',
model: 'marble-1.1',
world_prompt: {
type: 'text',
text_prompt: 'A mystical forest with glowing mushrooms'
}
})
});
const data = await response.json();
console.log(data);
```
```python Request theme={null}
import requests
url = "https://api.worldlabs.ai/marble/v1/worlds:generate"
payload = {
"display_name": "Mystical Forest",
"model": "marble-1.1",
"world_prompt": {
"type": "text",
"text_prompt": "A mystical forest with glowing mushrooms"
}
}
headers = {
"WLT-Api-Key": "YOUR_API_KEY",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)
```
This will return an Operation object.
```json Response theme={null}
{
"operation_id": "20bffbb1-4ba7-453f-a116-93eaw1a6843e",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z",
"expires_at": "2025-01-15T11:30:00Z",
"done": false,
"error": null,
"metadata": null,
"response": null
}
```
Poll the [`/marble/v1/operations/{operation_id}`](/api/reference/operations/get) endpoint until the operation is done.
```bash Request theme={null}
curl -X GET 'https://api.worldlabs.ai/marble/v1/operations/20bffbb1-4ba7-453f-a116-93eaw1a6843e' \
-H 'WLT-Api-Key: YOUR_API_KEY'
```
```javascript Request theme={null}
const response = await fetch('https://api.worldlabs.ai/marble/v1/operations/20bffbb1-4ba7-453f-a116-93eaw1a6843e', {
method: 'GET',
headers: {
'WLT-Api-Key': 'YOUR_API_KEY'
}
});
const data = await response.json();
console.log(data);
```
```python Request theme={null}
import requests
url = "https://api.worldlabs.ai/marble/v1/operations/20bffbb1-4ba7-453f-a116-93eaw1a6843e"
headers = {
"WLT-Api-Key": "YOUR_API_KEY"
}
response = requests.get(url, headers=headers)
print(response.text)
```
This will return an Operation object. If the operation is not done, it will return a `200` status code and the Operation object will have a `done` field set to `false`:
```json Response theme={null}
{
"operation_id": "20bffbb1-4ba7-453f-a116-93eaw1a6843e",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z",
"expires_at": "2025-01-15T11:30:00Z",
"done": false,
"error": null,
"metadata": {
"progress": { "status": "IN_PROGRESS", "description": "World generation in progress" },
"world_id": "dc2c65e4-68d3-4210-a01e-7a54cc9ded2a"
},
"response": null
}
```
World generation should take **about 5 minutes** to complete. Once the world is generated, the `done` field will be set to `true` and the `response` field will contain the generated World:
```json Response theme={null}
{
"operation_id": "20bffbb1-4ba7-453f-a116-93eab1a6843e",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:35:00Z",
"expires_at": "2025-01-15T11:30:00Z",
"done": true,
"error": null,
"metadata": {
"progress": {
"status": "SUCCEEDED",
"description": "World generation completed successfully"
},
"world_id": "dc2c65e4-68d3-4210-a01e-7a54cc9ded2a"
},
"response": {
"id": "dc2c65e4-68d3-4210-a01e-7a54cc9ded2a",
"display_name": "",
"tags": null,
"world_marble_url": "https://marble.worldlabs.ai/world/dc2c65e4-68d3-4210-a01e-7a54cc9ded2a",
"assets": {
"caption": "The scene is a fantastical forest...",
"thumbnail_url": "",
"splats": {
"spz_urls": {
"500k": "<500k_spz_url>",
"100k": "<100k_spz_url>",
"full_res": ""
},
"semantics_metadata": {
"metric_scale_factor": 1.23,
"ground_plane_offset": 0.42
}
},
"mesh": {
"collider_mesh_url": ""
},
"imagery": {
"pano_url": ""
}
},
"created_at": null,
"updated_at": null,
"permission": null,
"world_prompt": null,
"model": null
}
}
```
The `response` field contains a snapshot of the World at the time the operation completed. This allows you to access the generated assets without making a separate API call. Note that some fields like `display_name`, `created_at`, `updated_at`, `world_prompt`, and `model` may be empty or null in this snapshot. Use the [`GET /marble/v1/worlds/{world_id}`](/api/reference/worlds/get) endpoint to fetch the complete, up-to-date world.
You can view the generated world in Marble at `https://marble.worldlabs.ai/world/{world_id}`.
If you need to fetch the most up-to-date version of the world later, use the `world_id` to retrieve it.
```bash Request theme={null}
curl -X GET 'https://api.worldlabs.ai/marble/v1/worlds/dc2c65e4-68d3-4210-a01e-7a54cc9ded2a' \
-H 'WLT-Api-Key: YOUR_API_KEY'
```
```javascript Request theme={null}
const response = await fetch('https://api.worldlabs.ai/marble/v1/worlds/dc2c65e4-68d3-4210-a01e-7a54cc9ded2a', {
method: 'GET',
headers: {
'WLT-Api-Key': 'YOUR_API_KEY'
}
});
const data = await response.json();
console.log(data);
```
```python Request theme={null}
import requests
url = "https://api.worldlabs.ai/marble/v1/worlds/dc2c65e4-68d3-4210-a01e-7a54cc9ded2a"
headers = {
"WLT-Api-Key": "YOUR_API_KEY"
}
response = requests.get(url, headers=headers)
print(response.text)
```
This returns the latest version of the world:
```json Response theme={null}
{
"world": {
"id": "dc2c65e4-68d3-4210-a01e-7a54cc9ded2a",
"display_name": "Mystical Forest",
"tags": null,
"world_marble_url": "https://marble.worldlabs.ai/world/dc2c65e4-68d3-4210-a01e-7a54cc9ded2a",
"assets": {
"caption": "The scene is a fantastical forest...",
"thumbnail_url": "",
"splats": {
"spz_urls": {
"500k": "<500k_spz_url>",
"full_res": "",
"100k": "<100k_spz_url>"
},
"semantics_metadata": {
"metric_scale_factor": 1.23,
"ground_plane_offset": 0.42
}
},
"mesh": {
"collider_mesh_url": ""
},
"imagery": {
"pano_url": ""
}
},
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:35:00Z",
"permission": null,
"world_prompt": {
"type": "text",
"text_prompt": "The scene is a fantastical forest..."
},
"model": "marble-1.1"
}
}
```
The world object includes:
* `assets.splats.spz_urls`: 3D Gaussian splat files in SPZ format (100k, 500k, and full resolution)
* `assets.splats.semantics_metadata`: Per-world scale and ground-plane fields for metric splat rendering
* `assets.mesh.collider_mesh_url`: Collider mesh in GLB format
* `assets.mesh.hq_mesh_url`: High-quality textured mesh in GLB format, once exported
* `assets.mesh.full_res_mesh_url`: Full-resolution vertex-colored mesh in GLB format, once exported
* `assets.imagery.pano_url`: Panorama image
* `assets.caption`: AI-generated description of the world
* `assets.thumbnail_url`: Thumbnail image for the world
* `world_prompt`: The prompt used to generate the world (may be recaptioned)
* `model`: The model used for generation
The `semantics_metadata` numbers shown above are example values. Use
the values returned for the world you are rendering.
To download additional formats, call [`POST /marble/v1/worlds/{world_id}:export`](/api/reference/worlds/export).
Use `{"asset_type":"splats","format":"ply"}` for a cached `.ply` splat download, or
`{"asset_type":"mesh","format":"glb"}` to start a high-quality mesh export.
See [Rendering Marble SPZ files in third-party engines](/api/rendering-spz)
for the splat scale formula.
You can create a world from a single image using either a public URL or by uploading a local file.
Recommended image formats: `jpg`, `jpeg`, `png`, `webp`.
If your image is already hosted at a public URL, you can reference it directly.
Make a `POST` request to the [`/marble/v1/worlds:generate`](/api/reference/worlds/generate) endpoint with your image URL.
```bash Request theme={null}
curl -X POST 'https://api.worldlabs.ai/marble/v1/worlds:generate' \
-H 'Content-Type: application/json' \
-H 'WLT-Api-Key: YOUR_API_KEY' \
-d '{
"display_name": "My Image World",
"world_prompt": {
"type": "image",
"image_prompt": {
"source": "uri",
"uri": "https://example.com/my-image.jpg"
},
"text_prompt": "A beautiful landscape"
}
}'
```
```javascript Request theme={null}
const response = await fetch('https://api.worldlabs.ai/marble/v1/worlds:generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'WLT-Api-Key': 'YOUR_API_KEY'
},
body: JSON.stringify({
display_name: 'My Image World',
world_prompt: {
type: 'image',
image_prompt: {
source: 'uri',
uri: 'https://example.com/my-image.jpg'
},
text_prompt: 'A beautiful landscape'
}
})
});
const data = await response.json();
console.log(data);
```
```python Request theme={null}
import requests
url = "https://api.worldlabs.ai/marble/v1/worlds:generate"
payload = {
"display_name": "My Image World",
"world_prompt": {
"type": "image",
"image_prompt": {
"source": "uri",
"uri": "https://example.com/my-image.jpg"
},
"text_prompt": "A beautiful landscape"
}
}
headers = {
"WLT-Api-Key": "YOUR_API_KEY",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)
```
This returns an Operation object. Poll the operation as shown in the text input example until `done` is `true`. The completed operation's `response` field will contain the generated World.
To use a local image file, first upload it as a media asset, then reference it in your generation request.
Make a `POST` request to [`/marble/v1/media-assets:prepare_upload`](/api/reference/media-assets/prepare-upload) to get a signed upload URL.
```bash Request theme={null}
curl -X POST 'https://api.worldlabs.ai/marble/v1/media-assets:prepare_upload' \
-H 'Content-Type: application/json' \
-H 'WLT-Api-Key: YOUR_API_KEY' \
-d '{
"file_name": "my-image.jpg",
"kind": "image",
"extension": "jpg"
}'
```
```javascript Request theme={null}
const response = await fetch('https://api.worldlabs.ai/marble/v1/media-assets:prepare_upload', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'WLT-Api-Key': 'YOUR_API_KEY'
},
body: JSON.stringify({
file_name: 'my-image.jpg',
kind: 'image',
extension: 'jpg'
})
});
const data = await response.json();
console.log(data);
```
```python Request theme={null}
import requests
url = "https://api.worldlabs.ai/marble/v1/media-assets:prepare_upload"
payload = {
"file_name": "my-image.jpg",
"kind": "image",
"extension": "jpg"
}
headers = {
"WLT-Api-Key": "YOUR_API_KEY",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)
```
This returns the media asset and upload information:
```json Response theme={null}
{
"media_asset": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"file_name": "my-image.jpg",
"kind": "image",
"extension": "jpg",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": null,
"metadata": null
},
"upload_info": {
"upload_url": "",
"upload_method": "PUT",
"required_headers": {
"x-goog-content-length-range": "0,1048576000"
}
}
}
```
Upload your image to the signed URL using the method and headers from the response.
```bash Request theme={null}
curl -X PUT '' \
-H 'x-goog-content-length-range: 0,1048576000' \
--data-binary '@/path/to/my-image.jpg'
```
```javascript Request theme={null}
const fs = require('fs');
const imageBuffer = fs.readFileSync('/path/to/my-image.jpg');
await fetch('', {
method: 'PUT',
headers: upload_info.required_headers,
body: imageBuffer
});
```
```python Request theme={null}
import requests
with open('/path/to/my-image.jpg', 'rb') as f:
image_data = f.read()
requests.put(
'',
headers=upload_info['required_headers'],
data=image_data
)
```
Use the `media_asset_id` from Step 1 to generate a world.
```bash Request theme={null}
curl -X POST 'https://api.worldlabs.ai/marble/v1/worlds:generate' \
-H 'Content-Type: application/json' \
-H 'WLT-Api-Key: YOUR_API_KEY' \
-d '{
"display_name": "My Image World",
"world_prompt": {
"type": "image",
"image_prompt": {
"source": "media_asset",
"media_asset_id": "550e8400-e29b-41d4-a716-446655440000"
},
"text_prompt": "A beautiful landscape"
}
}'
```
```javascript Request theme={null}
const response = await fetch('https://api.worldlabs.ai/marble/v1/worlds:generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'WLT-Api-Key': 'YOUR_API_KEY'
},
body: JSON.stringify({
display_name: 'My Image World',
world_prompt: {
type: 'image',
image_prompt: {
source: 'media_asset',
media_asset_id: '550e8400-e29b-41d4-a716-446655440000'
},
text_prompt: 'A beautiful landscape'
}
})
});
const data = await response.json();
console.log(data);
```
```python Request theme={null}
import requests
url = "https://api.worldlabs.ai/marble/v1/worlds:generate"
payload = {
"display_name": "My Image World",
"world_prompt": {
"type": "image",
"image_prompt": {
"source": "media_asset",
"media_asset_id": "550e8400-e29b-41d4-a716-446655440000"
},
"text_prompt": "A beautiful landscape"
}
}
headers = {
"WLT-Api-Key": "YOUR_API_KEY",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)
```
This returns an Operation object. Poll the operation as shown in the text input example until `done` is `true`. The completed operation's `response` field will contain the generated World.
The `text_prompt` field is optional. If omitted, a caption will be automatically generated from your image.
`is_pano` defaults to `auto`. In auto mode, the API detects valid equirectangular panorama inputs before generation. Set `is_pano` to `true` when you already know the image is a valid pano, or `false` when a wide image should be treated as a standard image.
You can create a world from multiple images of the same scene, each with an optional azimuth (horizontal angle in degrees).
Recommended image formats: `jpg`, `jpeg`, `png`, `webp`.
If your images are already hosted at public URLs, you can reference them directly.
Make a `POST` request to the [`/marble/v1/worlds:generate`](/api/reference/worlds/generate) endpoint with your image URLs and their azimuth positions.
```bash Request theme={null}
curl -X POST 'https://api.worldlabs.ai/marble/v1/worlds:generate' \
-H 'Content-Type: application/json' \
-H 'WLT-Api-Key: YOUR_API_KEY' \
-d '{
"display_name": "My Multi-Image World",
"world_prompt": {
"type": "multi-image",
"multi_image_prompt": [
{
"azimuth": 0,
"content": {
"source": "uri",
"uri": "https://example.com/front.jpg"
}
},
{
"azimuth": 180,
"content": {
"source": "uri",
"uri": "https://example.com/back.jpg"
}
}
],
"text_prompt": "A cozy living room"
}
}'
```
```javascript Request theme={null}
const response = await fetch('https://api.worldlabs.ai/marble/v1/worlds:generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'WLT-Api-Key': 'YOUR_API_KEY'
},
body: JSON.stringify({
display_name: 'My Multi-Image World',
world_prompt: {
type: 'multi-image',
multi_image_prompt: [
{
azimuth: 0,
content: {
source: 'uri',
uri: 'https://example.com/front.jpg'
}
},
{
azimuth: 180,
content: {
source: 'uri',
uri: 'https://example.com/back.jpg'
}
}
],
text_prompt: 'A cozy living room'
}
})
});
const data = await response.json();
console.log(data);
```
```python Request theme={null}
import requests
url = "https://api.worldlabs.ai/marble/v1/worlds:generate"
payload = {
"display_name": "My Multi-Image World",
"world_prompt": {
"type": "multi-image",
"multi_image_prompt": [
{
"azimuth": 0,
"content": {
"source": "uri",
"uri": "https://example.com/front.jpg"
}
},
{
"azimuth": 180,
"content": {
"source": "uri",
"uri": "https://example.com/back.jpg"
}
}
],
"text_prompt": "A cozy living room"
}
}
headers = {
"WLT-Api-Key": "YOUR_API_KEY",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)
```
This returns an Operation object. Poll the operation as shown in the text input example until `done` is `true`. The completed operation's `response` field will contain the generated World.
To use local image files, first upload each as a media asset, then reference them in your generation request.
For each image, prepare the upload and upload the file as shown in the [image input example](#from-local-file).
```bash Request theme={null}
# Prepare upload for first image
curl -X POST 'https://api.worldlabs.ai/marble/v1/media-assets:prepare_upload' \
-H 'Content-Type: application/json' \
-H 'WLT-Api-Key: YOUR_API_KEY' \
-d '{
"file_name": "front.jpg",
"kind": "image",
"extension": "jpg"
}'
# Upload the file to the returned upload_url
curl -X PUT '' \
-H 'Content-Type: image/jpeg' \
--data-binary '@/path/to/front.jpg'
# Repeat for each additional image
```
```javascript Request theme={null}
const fs = require('fs');
async function uploadImage(filePath, fileName) {
// Prepare upload
const prepareResponse = await fetch('https://api.worldlabs.ai/marble/v1/media-assets:prepare_upload', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'WLT-Api-Key': 'YOUR_API_KEY'
},
body: JSON.stringify({
file_name: fileName,
kind: 'image',
extension: 'jpg'
})
});
const { media_asset, upload_info } = await prepareResponse.json();
// Upload file
const imageBuffer = fs.readFileSync(filePath);
await fetch(upload_info.upload_url, {
method: 'PUT',
headers: upload_info.required_headers,
body: imageBuffer
});
return media_asset.id;
}
const frontId = await uploadImage('/path/to/front.jpg', 'front.jpg');
const backId = await uploadImage('/path/to/back.jpg', 'back.jpg');
```
```python Request theme={null}
import requests
def upload_image(file_path, file_name):
# Prepare upload
prepare_response = requests.post(
'https://api.worldlabs.ai/marble/v1/media-assets:prepare_upload',
headers={
'WLT-Api-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
json={
'file_name': file_name,
'kind': 'image',
'extension': 'jpg'
}
)
data = prepare_response.json()
media_asset = data['media_asset']
upload_info = data['upload_info']
# Upload file
with open(file_path, 'rb') as f:
requests.put(
upload_info['upload_url'],
headers=upload_info['required_headers'],
data=f.read()
)
return media_asset['id']
front_id = upload_image('/path/to/front.jpg', 'front.jpg')
back_id = upload_image('/path/to/back.jpg', 'back.jpg')
```
Use the media asset IDs to generate a world.
```bash Request theme={null}
curl -X POST 'https://api.worldlabs.ai/marble/v1/worlds:generate' \
-H 'Content-Type: application/json' \
-H 'WLT-Api-Key: YOUR_API_KEY' \
-d '{
"display_name": "My Multi-Image World",
"world_prompt": {
"type": "multi-image",
"multi_image_prompt": [
{
"azimuth": 0,
"content": {
"source": "media_asset",
"media_asset_id": ""
}
},
{
"azimuth": 180,
"content": {
"source": "media_asset",
"media_asset_id": ""
}
}
],
"text_prompt": "A cozy living room"
}
}'
```
```javascript Request theme={null}
const response = await fetch('https://api.worldlabs.ai/marble/v1/worlds:generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'WLT-Api-Key': 'YOUR_API_KEY'
},
body: JSON.stringify({
display_name: 'My Multi-Image World',
world_prompt: {
type: 'multi-image',
multi_image_prompt: [
{
azimuth: 0,
content: {
source: 'media_asset',
media_asset_id: frontId
}
},
{
azimuth: 180,
content: {
source: 'media_asset',
media_asset_id: backId
}
}
],
text_prompt: 'A cozy living room'
}
})
});
const data = await response.json();
console.log(data);
```
```python Request theme={null}
import requests
url = "https://api.worldlabs.ai/marble/v1/worlds:generate"
payload = {
"display_name": "My Multi-Image World",
"world_prompt": {
"type": "multi-image",
"multi_image_prompt": [
{
"azimuth": 0,
"content": {
"source": "media_asset",
"media_asset_id": front_id
}
},
{
"azimuth": 180,
"content": {
"source": "media_asset",
"media_asset_id": back_id
}
}
],
"text_prompt": "A cozy living room"
}
}
headers = {
"WLT-Api-Key": "YOUR_API_KEY",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)
```
This returns an Operation object. Poll the operation as shown in the text input example until `done` is `true`. The completed operation's `response` field will contain the generated World.
The `azimuth` field specifies the horizontal angle (in degrees) where the image was taken. Use `0` for front, `90` for right, `180` for back, `270` for left.
The `text_prompt` field is optional. If omitted, a caption will be automatically generated.
You can create a world from a video using either a public URL or by uploading a local file.
Recommended video formats: `mp4`, `mov`, `mkv`.
If your video is already hosted at a public URL, you can reference it directly.
Make a `POST` request to the [`/marble/v1/worlds:generate`](/api/reference/worlds/generate) endpoint with your video URL.
```bash Request theme={null}
curl -X POST 'https://api.worldlabs.ai/marble/v1/worlds:generate' \
-H 'Content-Type: application/json' \
-H 'WLT-Api-Key: YOUR_API_KEY' \
-d '{
"display_name": "My Video World",
"world_prompt": {
"type": "video",
"video_prompt": {
"source": "uri",
"uri": "https://example.com/my-video.mp4"
},
"text_prompt": "A scenic mountain landscape"
}
}'
```
```javascript Request theme={null}
const response = await fetch('https://api.worldlabs.ai/marble/v1/worlds:generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'WLT-Api-Key': 'YOUR_API_KEY'
},
body: JSON.stringify({
display_name: 'My Video World',
world_prompt: {
type: 'video',
video_prompt: {
source: 'uri',
uri: 'https://example.com/my-video.mp4'
},
text_prompt: 'A scenic mountain landscape'
}
})
});
const data = await response.json();
console.log(data);
```
```python Request theme={null}
import requests
url = "https://api.worldlabs.ai/marble/v1/worlds:generate"
payload = {
"display_name": "My Video World",
"world_prompt": {
"type": "video",
"video_prompt": {
"source": "uri",
"uri": "https://example.com/my-video.mp4"
},
"text_prompt": "A scenic mountain landscape"
}
}
headers = {
"WLT-Api-Key": "YOUR_API_KEY",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)
```
This returns an Operation object. Poll the operation as shown in the text input example until `done` is `true`. The completed operation's `response` field will contain the generated World.
To use a local video file, first upload it as a media asset, then reference it in your generation request.
Make a `POST` request to [`/marble/v1/media-assets:prepare_upload`](/api/reference/media-assets/prepare-upload) to get a signed upload URL.
```bash Request theme={null}
curl -X POST 'https://api.worldlabs.ai/marble/v1/media-assets:prepare_upload' \
-H 'Content-Type: application/json' \
-H 'WLT-Api-Key: YOUR_API_KEY' \
-d '{
"file_name": "my-video.mp4",
"kind": "video",
"extension": "mp4"
}'
```
```javascript Request theme={null}
const response = await fetch('https://api.worldlabs.ai/marble/v1/media-assets:prepare_upload', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'WLT-Api-Key': 'YOUR_API_KEY'
},
body: JSON.stringify({
file_name: 'my-video.mp4',
kind: 'video',
extension: 'mp4'
})
});
const data = await response.json();
console.log(data);
```
```python Request theme={null}
import requests
url = "https://api.worldlabs.ai/marble/v1/media-assets:prepare_upload"
payload = {
"file_name": "my-video.mp4",
"kind": "video",
"extension": "mp4"
}
headers = {
"WLT-Api-Key": "YOUR_API_KEY",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)
```
This returns the media asset and upload information:
```json Response theme={null}
{
"media_asset": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"file_name": "my-video.mp4",
"kind": "video",
"extension": "mp4",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": null,
"metadata": null
},
"upload_info": {
"upload_url": "",
"upload_method": "PUT",
"required_headers": {
"x-goog-content-length-range": "0,1048576000"
}
}
}
```
Upload your video to the signed URL using the method and headers from the response.
```bash Request theme={null}
curl -X PUT '' \
-H 'x-goog-content-length-range: 0,1048576000' \
--data-binary '@/path/to/my-video.mp4'
```
```javascript Request theme={null}
const fs = require('fs');
const videoBuffer = fs.readFileSync('/path/to/my-video.mp4');
await fetch('', {
method: 'PUT',
headers: upload_info.required_headers,
body: videoBuffer
});
```
```python Request theme={null}
import requests
with open('/path/to/my-video.mp4', 'rb') as f:
video_data = f.read()
requests.put(
'',
headers=upload_info['required_headers'],
data=video_data
)
```
Use the `media_asset_id` from Step 1 to generate a world.
```bash Request theme={null}
curl -X POST 'https://api.worldlabs.ai/marble/v1/worlds:generate' \
-H 'Content-Type: application/json' \
-H 'WLT-Api-Key: YOUR_API_KEY' \
-d '{
"display_name": "My Video World",
"world_prompt": {
"type": "video",
"video_prompt": {
"source": "media_asset",
"media_asset_id": "550e8400-e29b-41d4-a716-446655440000"
},
"text_prompt": "A scenic mountain landscape"
}
}'
```
```javascript Request theme={null}
const response = await fetch('https://api.worldlabs.ai/marble/v1/worlds:generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'WLT-Api-Key': 'YOUR_API_KEY'
},
body: JSON.stringify({
display_name: 'My Video World',
world_prompt: {
type: 'video',
video_prompt: {
source: 'media_asset',
media_asset_id: '550e8400-e29b-41d4-a716-446655440000'
},
text_prompt: 'A scenic mountain landscape'
}
})
});
const data = await response.json();
console.log(data);
```
```python Request theme={null}
import requests
url = "https://api.worldlabs.ai/marble/v1/worlds:generate"
payload = {
"display_name": "My Video World",
"world_prompt": {
"type": "video",
"video_prompt": {
"source": "media_asset",
"media_asset_id": "550e8400-e29b-41d4-a716-446655440000"
},
"text_prompt": "A scenic mountain landscape"
}
}
headers = {
"WLT-Api-Key": "YOUR_API_KEY",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)
```
This returns an Operation object. Poll the operation as shown in the text input example until `done` is `true`. The completed operation's `response` field will contain the generated World.
The `text_prompt` field is optional. If omitted, a caption will be automatically generated from your video.
# Interactive World Examples
Source: https://docs.worldlabs.ai/api/interactive-world-examples
Open-source examples that combine World Labs worlds with realtime rendering, physics, controllers, and creative tooling.
Build interactive experiences by combining Marble-generated assets with realtime rendering, physics, and game controls. These examples show how developers can splats, collider meshes, and browser-based tooling for more interactive worlds.
Add dynamic objects to an interactive scene.
A tavern demo that combines Spark Gaussian splat rendering, Rapier physics, and Three.js. It includes first-person controls, projectile collisions, animated characters, spatial audio, and a debug view for the collision mesh.
A Claude skillset that turns a single image into a 3D environment workflow, including static Gaussian splats, dynamic object meshes, and audio assets.
A browser-based tool for loading `.spz` or `.rad` splat scenes, drawing box, sphere, and cylinder colliders, and exporting collision geometry as `.glb` files.
A third-person character controller template using Spark 2.0 LoD, Three.js, and Rapier physics. It includes a kinematic controller, camera controls, animation blending, and collider mesh support.
A physics-based first-person controller for navigating Gaussian splat scenes with SparkJS, Three.js, and Rapier. It includes collision, jumping, gamepad support, a ball shooter, post-processing, and debug controls.
# Models
Source: https://docs.worldlabs.ai/api/models
How Marble model names map to World API model parameters
## Model mapping
The World API currently supports the following Marble models:
| Marble model | API `model` |
| ------------------ | ------------------ |
| `Marble 1.1 Plus` | `marble-1.1-plus` |
| `Marble 1.1` | `marble-1.1` |
| `Marble 1.0` | `marble-1.0` |
| `Marble 1.0 Draft` | `marble-1.0-draft` |
The API still supports the legacy `model` value for Marble 1.0 models. Support for these legacy values will be removed in a future release.
| Current `model` value | Legacy `model` value |
| --------------------- | -------------------- |
| `marble-1.0` | `Marble 0.1-plus` |
| `marble-1.0-draft` | `Marble 0.1-mini` |
The API currently supports a default model of `marble-1.0` if no model is specified in order to maintain compability. This will change in a future release to default to `marble-1.1`.
# Pricing
Source: https://docs.worldlabs.ai/api/pricing
Understanding API usage and credits
## Credits
World API billing is separate from Marble web app billing.
* Credits purchased for the Marble app at [marble.worldlabs.ai](https://marble.worldlabs.ai) CANNOT be used with the API
* API usage requires credits purchased through the World Labs Platform at [platform.worldlabs.ai](https://platform.worldlabs.ai)
If you plan to use the API, make sure you purchase credits on the World Labs Platform, not in the Marble app.
The World Labs API uses a credit-based pricing model.
You may purchase credits at a fixed rate of \$1.00 USD per 1,250 credits through the [World Labs Platform](https://platform.worldlabs.ai/billing). The minimum purchase is 6,250 credits or \$5.00 USD.
API credits do not expire.
You can check your current API credit balance with
[`GET /marble/v1/credits`](/api/reference/credits/get).
### Auto-refill
You may enable auto-refill to avoid service interruptions by automatically purchasing credits when your balance is low.
On the [billing page](https://platform.worldlabs.ai/billing), you may enable and configure auto-refill once you have a payment method on file.
You may configure the threshold at which auto-refill is triggered, as well as the target balance to refill to.
Note that when auto-refill is triggered, your balance will not settle at the target balance. This is because the refill is applied before the cost of the API request is deducted from your balance.
For example, assume your threshold is 10,000 credits and your target balance is 20,000 credits, and you have a balance of 10,000 credits.
1. You make an API request that costs 1,500 credits. We would observe that your balance would drop to 8,500 credits, which is below your threshold.
2. The auto-refill would then be triggered and you would be charged to bring your balance to 20,000 credits.
3. Finally, your balance would drop to 18,500 credits to charge for the API request.
## Usage events
Credits are consumed as you use the API.
API requests may map to one or more usage events, and each usage event may have its own cost in credits associated with it. The total cost of an API request is the sum of the costs of all the usage events it maps to. The cost of each usage event is determined largely by the compute resources required to complete the underlying operation.
You may view your usage event history in the [usage page](https://platform.worldlabs.ai/usage).
Note that not all API requests consume credits, such as API key creation, media asset upload and management, and Operation polling.
If your credit balance is too low when a request arrives, the API returns
`402 Payment Required` with a response body that links to the
[billing page](https://platform.worldlabs.ai/billing), where you can add
credits or enable auto-refill.
### Overage
The credit check above compares your balance against a low-balance threshold.
It is not a per-request cost estimate, so it does not guarantee that your
remaining balance covers the request it admits. A request that starts while
your balance is low can cost more credits than you have left and take your
balance below zero.
Credits used beyond your prepaid balance are not refused and not written off.
They become an overage. Unlike prepaid credits, which you pay for up front and
draw down as you go, an overage is settled after the fact: at the end of each
monthly billing period, usage your prepaid credits did not cover is converted
to USD at your account's credit rate — \$1.00 USD per 1,250 credits on standard
pay-as-you-go accounts, the same rate at which credits are sold — and charged
to your payment method on file.
Two things about overage charges surprise people:
* **The charge arrives after the usage.** Usage in one month is invoiced at the
start of the next, so a charge can land weeks after your last API request.
* **Auto-refill does not gate it.** Auto-refill only controls whether we top up
your balance automatically. Turning it off does not cap your usage and does
not prevent an end-of-period overage charge.
To check what an overage charge covers, compare the billing period shown on the
invoice against your usage events for that period on the
[usage page](https://platform.worldlabs.ai/usage).
### World generation pricing
Generating a world using the [World Generation API](/api/reference/worlds/generate) is the most common API request. However, the number of usage events and the cost of generating a world depends on the input type.
The World Generation API requires a panorama image (pano) to convert into a 3D world, so it first generates a pano from your input if a pano is not provided. Image prompts use `is_pano: "auto"` by default; when the API detects a valid equirectangular pano, it skips pano generation. As a result, a World Generation API request often includes two usage events:
1. Pano generation (if needed)
2. World generation (from pano)
For **Marble 1.0 / `marble-1.0`** and **Marble 1.1 / `marble-1.1`**, the **world generation** usage event is billed at **1,500 credits**. Draft world generation (`marble-1.0-draft`) is billed at **150 credits**.
For **Marble 1.1 Plus / `marble-1.1-plus`**, a request always includes **1 world generation** event billed at **1,500 credits**. For larger worlds, our system may automatically determine to include up to 1,500 additional credits for variable world generation.
Depending on your input type, you may also incur a **pano generation** usage event. If you generate from an existing pano and the API treats it as a pano, there is no pano generation step, so there is no additional pano generation cost.
#### Usage event costs
| Usage event | Credits |
| --------------------------------- | ------: |
| Pano generation (image, pano) | 0 |
| Pano generation (text) | 80 |
| Pano generation (image, non-pano) | 80 |
| Pano generation (multi-image) | 100 |
| Pano generation (video) | 100 |
| Draft world generation | 150 |
| World generation | 1,500 |
| HQ mesh export | 3,500 |
#### Draft model totals (`marble-1.0-draft`)
| Input type | Pano generation | World generation | Total |
| ---------------- | --------------: | ---------------: | ----: |
| Image (pano) | 0 | 150 | 150 |
| Text | 80 | 150 | 230 |
| Image (non-pano) | 80 | 150 | 230 |
| Multi-image | 100 | 150 | 250 |
| Video | 100 | 150 | 250 |
#### Standard model totals (`marble-1.0`, `marble-1.1`)
| Input type | Pano generation | World generation | Total |
| ---------------- | --------------: | ---------------: | ----: |
| Image (pano) | 0 | 1,500 | 1,500 |
| Text | 80 | 1,500 | 1,580 |
| Image (non-pano) | 80 | 1,500 | 1,580 |
| Multi-image | 100 | 1,500 | 1,600 |
| Video | 100 | 1,500 | 1,600 |
#### Marble 1.1 Plus totals (`marble-1.1-plus`)
| Input type | Pano generation | Base world generation | Variable world generation | Total |
| ---------------- | --------------: | --------------------: | ------------------------- | ----------- |
| Image (pano) | 0 | 1,500 | 0-1,500 | 1,500-3,000 |
| Text | 80 | 1,500 | 0-1,500 | 1,580-3,080 |
| Image (non-pano) | 80 | 1,500 | 0-1,500 | 1,580-3,080 |
| Multi-image | 100 | 1,500 | 0-1,500 | 1,600-3,100 |
| Video | 100 | 1,500 | 0-1,500 | 1,600-3,100 |
#### Observed Marble 1.1 Plus costs
The variable world generation charge depends on the size of the world,
so actual costs vary by request. The table below shows observed succeeded
`marble-1.1-plus` World Generation API costs through June 25, 2026.
1,250 credits = \$1.00.
| Input type | Mean | Median | P75 | P90 | P95 | P99 | Max |
| ---------------- | ------: | ------: | ------: | ------: | ------: | ------: | ------: |
| Image (pano) | \$1.707 | \$1.200 | \$2.400 | \$2.400 | \$2.400 | \$2.400 | \$2.400 |
| Text | \$1.600 | \$1.264 | \$2.224 | \$2.464 | \$2.464 | \$2.464 | \$2.464 |
| Image (non-pano) | \$1.755 | \$1.264 | \$2.464 | \$2.464 | \$2.464 | \$2.464 | \$2.464 |
| Multi-image | \$1.770 | \$1.280 | \$2.480 | \$2.480 | \$2.480 | \$2.480 | \$2.480 |
| Video | \$1.735 | \$1.280 | \$2.480 | \$2.480 | \$2.480 | \$2.480 | \$2.480 |
Overall `marble-1.1-plus` costs across these input types:
* Mean: \$1.714
* Median: \$1.280
* P75/P90: \$2.464
* P95/P99/max: \$2.480
### Mesh export pricing
Exporting a high-quality mesh with the [Export API](/api/reference/worlds/export)
(`{"asset_type": "mesh", "format": "glb"}`) is billed as a single **HQ mesh
export** usage event of **3,500 credits** (\$2.80).
A single export run produces both mesh variants (`textured` and
`vertex_colored`), so you are charged once per world, not once per variant.
You are only charged when a new export run starts:
* If the world already has an exported mesh, the API returns the cached
result at no cost.
* If a mesh export is already running for the world, the API returns the
existing operation at no cost.
PLY splat exports (`{"asset_type": "splats", "format": "ply"}`) are free.
| Usage event | Credits |
| -------------- | ------: |
| HQ mesh export | 3,500 |
# Rate limits
Source: https://docs.worldlabs.ai/api/rate-limits
Rate limits and throughput options for World API world generation
## Rate limits
World API rate limits protect capacity and keep world generation available for all users. The limits on this page apply to requests that start a world generation with [`POST /marble/v1/worlds:generate`](/api/reference/worlds/generate).
There are two relevant types of limits:
1. **Start-rate limits** control how many world generation requests your API user can start over a time window.
2. **Throughput tiers** determine whether your account uses the default limits or an approved higher-throughput configuration.
## Current limits
| Tier | Requests covered | Limit |
| ------------------------------------ | -------------------------------- | ---------------------------------------------------- |
| Default | World generation starts | About 3 requests per minute and 60 requests per hour |
| Higher-throughput, approved accounts | Standard world generation starts | About 30 requests per minute |
| Higher-throughput, approved accounts | Draft world generation starts | About 90 requests per minute |
Standard world generation means non-draft models such as `marble-1.0`, `marble-1.1`, and `marble-1.1-plus`. Draft world generation means `marble-1.0-draft`.
Limits are enforced for the API user or account associated with the request, not for each API key.
## How limits are applied
* The limits on this page apply to generation starts, not to the number of generations already running.
* Usage is tracked in rolling windows, and enforcement is approximate. Short bursts can hit a limit even when average usage over the full window appears lower.
* These limits are maximum allowed usage, not guaranteed minimum throughput. Available capacity can vary with service load.
* Each accepted world generation usually takes about 5 minutes to complete. Use [`GET /marble/v1/operations/{operation_id}`](/api/reference/operations/get) to poll for the result.
## 429 errors
If you exceed a rate limit, the API returns `429 Too Many Requests`. Treat a `429` as a signal to slow generation starts for that API user or account.
When handling `429` responses:
1. Pause before retrying. If the response includes `Retry-After`, wait at least that long.
2. Retry with exponential backoff and jitter.
3. Avoid retrying many queued starts at once after a pause.
4. Reduce concurrent start attempts if you continue seeing `429` responses.
A generation request that returns `429` has not been accepted. After retrying successfully, the response will contain an Operation object; poll that operation instead of resubmitting the same start request.
## Requesting higher limits
If your workload needs sustained concurrent generation starts, contact [support@worldlabs.ai](mailto:support@worldlabs.ai) or your World Labs account representative with:
* The API user or account email.
* The models you plan to use.
* Expected starts per minute and starts per hour.
* Whether traffic is bursty or sustained.
* A short description of the customer workflow or launch timeline.
Higher limits are reviewed per account and may use separate standard and draft world generation limits.
# Get credits
Source: https://docs.worldlabs.ai/api/reference/credits/get
GET /marble/v1/credits
Get remaining API credits for the authenticated user.
Returns:
CreditsResponse with the current aggregate API credit balance.
Raises:
HTTPException: 404 if the caller is not an API-enabled user
HTTPException: 503 if the billing backend is unavailable
# Credits
Source: https://docs.worldlabs.ai/api/reference/credits/index
Check the API credit balance for the authenticated account.
Credits endpoints expose billing state needed by API clients before starting
work that consumes credits.
**GET** `/marble/v1/credits`
Return the current aggregate API credit balance for the authenticated user.
Use this endpoint before submitting expensive jobs when your client needs to
warn users about low balance or pause automated submissions.
# API reference
Source: https://docs.worldlabs.ai/api/reference/index
Browse Marble Public API endpoints by resource.
The Marble Public API is organized around the resources you use to create and
manage generated worlds.
Create worlds, list API-generated worlds, retrieve generated assets, and
delete worlds you own.
Prepare uploads for input images or videos, then reference those assets in
world generation requests.
Poll long-running generation and pano jobs until they complete or fail.
Generate an RGB panorama from depth input and a text description.
Check the remaining API credit balance for the authenticated account.
## Common workflow
Use [Prepare media asset upload](/api/reference/media-assets/prepare-upload)
for local images or videos that should become world-generation inputs.
Use [Generate a world](/api/reference/worlds/generate) with a supported
world prompt.
Use [Get an operation](/api/reference/operations/get) with the returned
`operation_id` until `done` is `true`.
Use [Get a world](/api/reference/worlds/get) to fetch the final world
metadata and generated asset URLs.
# Get media asset
Source: https://docs.worldlabs.ai/api/reference/media-assets/get
GET /marble/v1/media-assets/{media_asset_id}
Get a media asset by ID.
Retrieves metadata for a previously created media asset.
Args:
media_asset_id: The media asset identifier.
Returns:
MediaAsset object with media_asset_id, file_name, extension, kind,
metadata, created_at, and updated_at.
Raises:
HTTPException: 404 if not found
# Media assets
Source: https://docs.worldlabs.ai/api/reference/media-assets/index
Upload input media for world generation requests.
Media asset endpoints let you upload images or videos to managed storage before
using them as world-generation inputs.
**POST** `/marble/v1/media-assets:prepare_upload`
Create a media asset record and receive the signed upload URL and required
headers.
**GET** `/marble/v1/media-assets/{media_asset_id}`
Retrieve metadata for an uploaded media asset.
## Upload flow
Call [Prepare media asset upload](/api/reference/media-assets/prepare-upload)
with the file name, extension, and media kind.
Send the file to the returned signed URL with the required headers from the
response.
Use the returned `media_asset_id` in
[Generate a world](/api/reference/worlds/generate).
# Prepare media asset upload
Source: https://docs.worldlabs.ai/api/reference/media-assets/prepare-upload
POST /marble/v1/media-assets:prepare_upload
Prepare a media asset upload for use in world generation.
This API endpoint creates a media asset record and returns a signed upload URL.
Use this workflow to upload images or videos that you want to reference in world
generation requests.
## Workflow
1. **Prepare Upload** (this endpoint): Get a `media_asset_id` and `upload_url`
2. **Upload File**: Use the signed URL to upload your file
3. **Generate World**: Reference the `media_asset_id` in `/worlds:generate` with
source type "media_asset"
## Request Parameters
- `file_name`: Your file's name (e.g., "landscape.jpg")
- `extension`: File extension without dot (e.g., "jpg", "png", "mp4")
- `kind`: Either "image" or "video"
- `metadata`: Optional custom metadata object
## Response
Returns a `MediaAssetPrepareUploadResponse` containing:
- `media_asset`: Object with `media_asset_id` (use this in world generation)
- `upload_info`: Object with `upload_url`, `required_headers`, and `curl_example`
## Uploading Your File
Use the returned `upload_url` and `required_headers` to upload your file:
```bash
curl --request PUT \
--url \
--header "Content-Type: " \
--header ": " \
--upload-file /path/to/your/file
```
Replace:
- ``: The `upload_url` from the response
- ``: MIME type (e.g., `image/png`, `image/jpeg`, `video/mp4`)
- `: `: Each header from `required_headers`
- `/path/to/your/file`: Path to your local file
## Example Usage in World Generation
After uploading, use the `media_asset_id` in a world generation request:
```json
{
"world_prompt": {
"type": "image",
"image_prompt": {
"source": "media_asset",
"media_asset_id": ""
}
}
}
```
# OpenAPI spec
Source: https://docs.worldlabs.ai/api/reference/openapi
View the OpenAPI specification file
We use an OpenAPI spec to generate endpoint documentation. You can consume the
spec directly or browse API reference pages.
````yaml theme={null}
components:
schemas:
Content:
description: 'Represents content (media, text, images) that can be stored inline
or via URL.
Supports both direct data storage (up to 10MB) and URL references (up to 20MB).'
properties:
data_base64:
anyOf:
- type: string
- type: 'null'
title: Data Base64
extension:
anyOf:
- type: string
- type: 'null'
description: File extension without dot
examples:
- jpg
- png
- pdf
- txt
title: Extension
uri:
anyOf:
- type: string
- type: 'null'
title: Uri
title: Content
type: object
CreditsResponse:
description: Remaining credits for the authenticated API user.
properties:
remaining_credits:
description: Remaining API credits available to the authenticated user.
minimum: 0.0
title: Remaining Credits
type: number
required:
- remaining_credits
title: CreditsResponse
type: object
DataBase64Reference:
description: Reference to content via base64-encoded data.
properties:
data_base64:
description: Base64-encoded content data
title: Data Base64
type: string
extension:
anyOf:
- type: string
- type: 'null'
description: File extension without dot (e.g., 'jpg', 'png')
title: Extension
source:
const: data_base64
default: data_base64
title: Source
type: string
required:
- data_base64
title: DataBase64Reference
type: object
DeleteWorldResponse:
description: Response from world deletion endpoint.
properties:
deleted:
description: Whether the world was successfully deleted
title: Deleted
type: boolean
world_id:
description: ID of the deleted world
title: World Id
type: string
required:
- world_id
- deleted
title: DeleteWorldResponse
type: object
DepthPanoPrompt:
description: 'For models conditioned on a depth pano and text.
When depth_pano_image is a log-encoded PNG, z_min and z_max are required
to decode it correctly. When it is an EXR file containing raw float depth
values, z_min and z_max will both be ignored.
Please set z_min and z_max both to None when using an EXR file.'
properties:
depth_pano_image:
$ref: '#/components/schemas/Content'
text_prompt:
anyOf:
- type: string
- type: 'null'
title: Text Prompt
type:
const: depth-pano
default: depth-pano
title: Type
type: string
z_max:
anyOf:
- type: number
- type: 'null'
title: Z Max
z_min:
anyOf:
- type: number
- type: 'null'
title: Z Min
required:
- depth_pano_image
title: DepthPanoPrompt
type: object
GenerateWorldResponse:
description: Response from world generation endpoint.
properties:
cost:
anyOf:
- $ref: '#/components/schemas/OperationCost'
- type: 'null'
description: Settled credit cost for the operation. Populated only on successful
completion.
created_at:
anyOf:
- format: date-time
type: string
- type: 'null'
description: Creation timestamp
title: Created At
done:
description: True if the operation is completed
title: Done
type: boolean
error:
anyOf:
- $ref: '#/components/schemas/OperationError'
- type: 'null'
description: Error information if the operation failed
expires_at:
anyOf:
- format: date-time
type: string
- type: 'null'
description: Expiration timestamp
title: Expires At
metadata:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
description: Service-specific metadata, such as progress percentage
title: Metadata
operation_id:
description: Operation identifier
title: Operation Id
type: string
response:
anyOf:
- {}
- type: 'null'
description: Result payload when done=true and no error. Structure depends
on operation type.
title: Response
updated_at:
anyOf:
- format: date-time
type: string
- type: 'null'
description: Last update timestamp
title: Updated At
required:
- operation_id
- done
title: GenerateWorldResponse
type: object
GetOperationResponse_Union_World__PanoDepthToRgbResult__:
properties:
cost:
anyOf:
- $ref: '#/components/schemas/OperationCost'
- type: 'null'
description: Settled credit cost for the operation. Populated only on successful
completion.
created_at:
anyOf:
- format: date-time
type: string
- type: 'null'
description: Creation timestamp
title: Created At
done:
description: True if the operation is completed
title: Done
type: boolean
error:
anyOf:
- $ref: '#/components/schemas/OperationError'
- type: 'null'
description: Error information if the operation failed
expires_at:
anyOf:
- format: date-time
type: string
- type: 'null'
description: Expiration timestamp
title: Expires At
metadata:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
description: Service-specific metadata, such as progress percentage
title: Metadata
operation_id:
description: Operation identifier
title: Operation Id
type: string
response:
anyOf:
- $ref: '#/components/schemas/World'
- $ref: '#/components/schemas/PanoDepthToRgbResult'
- type: 'null'
description: Result payload when done=true and no error. Structure depends
on operation type.
title: Response
updated_at:
anyOf:
- format: date-time
type: string
- type: 'null'
description: Last update timestamp
title: Updated At
required:
- operation_id
- done
title: GetOperationResponse[Union[World, PanoDepthToRgbResult]]
type: object
HTTPValidationError:
properties:
detail:
items:
$ref: '#/components/schemas/ValidationError'
title: Detail
type: array
title: HTTPValidationError
type: object
ImagePrompt:
description: 'Image-to-world generation.
Generates a world from an image. text_prompt is optional - if not provided,
it will be generated via recaptioning.
Recommended image formats: jpg, jpeg, png, webp.'
properties:
disable_recaption:
anyOf:
- type: boolean
- type: 'null'
description: If True, use text_prompt as-is without recaptioning
title: Disable Recaption
image_prompt:
description: Image content for world generation
discriminator:
mapping:
data_base64: '#/components/schemas/DataBase64Reference'
media_asset: '#/components/schemas/MediaAssetReference'
uri: '#/components/schemas/UriReference'
propertyName: source
oneOf:
- $ref: '#/components/schemas/MediaAssetReference'
- $ref: '#/components/schemas/UriReference'
- $ref: '#/components/schemas/DataBase64Reference'
title: Image Prompt
is_pano:
$ref: '#/components/schemas/PanoDetectionMode'
default: auto
description: How to treat the image input as a panorama. `auto` detects
valid equirectangular panoramas, `true` always uses the image as a panorama,
and `false` treats it as a standard image.
text_prompt:
anyOf:
- type: string
- type: 'null'
description: Optional text guidance (auto-generated if not provided)
title: Text Prompt
type:
const: image
default: image
title: Type
type: string
required:
- image_prompt
title: ImagePrompt
type: object
ImageryAssets:
description: Imagery asset URLs.
properties:
pano_url:
anyOf:
- type: string
- type: 'null'
description: Panorama image URL
title: Pano Url
title: ImageryAssets
type: object
InpaintPanoPrompt:
description: For models that inpaint the masked portion of a pano image.
properties:
pano_image:
$ref: '#/components/schemas/Content'
pano_mask:
$ref: '#/components/schemas/Content'
text_prompt:
anyOf:
- type: string
- type: 'null'
title: Text Prompt
type:
const: inpaint-pano
default: inpaint-pano
title: Type
type: string
required:
- pano_image
- pano_mask
title: InpaintPanoPrompt
type: object
ListWorldsRequest:
description: Request to list API-generated worlds with optional filters.
examples:
- model: marble-1.0
page_size: 20
sort_by: created_at
status: SUCCEEDED
- page_size: 50
sort_by: created_at
status: SUCCEEDED
tags:
- fantasy
- nature
- created_after: '2024-01-01T00:00:00Z'
created_before: '2024-12-31T23:59:59Z'
page_size: 100
sort_by: created_at
- model: marble-1.0-draft
page_size: 30
tags:
- landscape
- page_size: 20
page_token: eyJzbmFwc2hvdF90aW1lIjoiMjAyNC0wMS0wMVQwMDowMDowMFoiLCJsYXN0X2NyZWF0ZWRfYXQiOiIyMDI0LTAxLTAxVDAwOjAwOjAwWiIsImxhc3RfaWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDAifQ==
sort_by: created_at
properties:
created_after:
anyOf:
- format: date-time
type: string
- type: 'null'
description: Filter worlds created after this timestamp (inclusive)
title: Created After
created_before:
anyOf:
- format: date-time
type: string
- type: 'null'
description: Filter worlds created before this timestamp (exclusive)
title: Created Before
is_public:
anyOf:
- type: boolean
- type: 'null'
description: Filter by visibility. true=public only, false=private only,
null=all
title: Is Public
model:
anyOf:
- enum:
- marble-1.0-draft
- marble-1.0
- marble-1.1
- marble-1.1-plus
type: string
- enum:
- Marble 0.1-mini
- Marble 0.1-plus
- Marble 1.1-plus
type: string
- type: 'null'
description: Filter by model used for generation. Legacy names are deprecated;
use the new lowercase names.
title: Model
page_size:
default: 20
description: Number of results per page (1-100)
maximum: 100.0
minimum: 1.0
title: Page Size
type: integer
page_token:
anyOf:
- type: string
- type: 'null'
description: Cursor token for pagination (opaque base64 string from previous
response). Use cursor from next_page_token in previous response for consistent
pagination.
title: Page Token
sort_by:
default: created_at
description: Sort results by created_at or updated_at
enum:
- created_at
- updated_at
title: Sort By
type: string
status:
anyOf:
- enum:
- SUCCEEDED
- PENDING
- FAILED
- RUNNING
type: string
- type: 'null'
description: Filter by world status
title: Status
tags:
anyOf:
- items:
type: string
type: array
- type: 'null'
description: Filter by tags (returns worlds with ANY of these tags)
title: Tags
title: ListWorldsRequest
type: object
ListWorldsResponse:
description: Response containing a list of API-generated worlds.
properties:
next_page_token:
anyOf:
- type: string
- type: 'null'
description: Token for fetching the next page of results
title: Next Page Token
worlds:
description: List of worlds
items:
$ref: '#/components/schemas/World'
title: Worlds
type: array
required:
- worlds
title: ListWorldsResponse
type: object
MediaAsset:
description: 'A user-uploaded media asset stored in managed storage.
MediaAssets can be images, videos, or binary blobs that are used
as input to world generation.'
properties:
created_at:
description: Creation timestamp
format: date-time
title: Created At
type: string
extension:
anyOf:
- type: string
- type: 'null'
description: File extension without dot
examples:
- mp4
- png
- jpg
title: Extension
file_name:
description: File name
title: File Name
type: string
kind:
$ref: '#/components/schemas/MediaAssetKind'
description: High-level media type
examples:
- image
- video
media_asset_id:
description: Server-generated media asset identifier
title: Media Asset Id
type: string
metadata:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
description: Optional application-specific metadata
title: Metadata
updated_at:
anyOf:
- format: date-time
type: string
- type: 'null'
description: Last update timestamp
title: Updated At
required:
- media_asset_id
- file_name
- kind
- created_at
title: MediaAsset
type: object
MediaAssetKind:
description: High-level media asset type.
enum:
- image
- video
title: MediaAssetKind
type: string
MediaAssetPrepareUploadRequest:
description: Request to prepare a media asset upload.
properties:
extension:
anyOf:
- type: string
- type: 'null'
description: File extension without dot
examples:
- mp4
- png
- jpg
title: Extension
file_name:
description: File name
maxLength: 64
title: File Name
type: string
kind:
$ref: '#/components/schemas/MediaAssetKind'
description: High-level media type
examples:
- image
- video
metadata:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
description: Optional application-specific metadata
title: Metadata
required:
- file_name
- kind
title: MediaAssetPrepareUploadRequest
type: object
MediaAssetPrepareUploadResponse:
description: Response from preparing a media asset upload.
properties:
media_asset:
$ref: '#/components/schemas/MediaAsset'
description: The created media asset
upload_info:
$ref: '#/components/schemas/UploadUrlInfo'
description: Upload URL information
required:
- media_asset
- upload_info
title: MediaAssetPrepareUploadResponse
type: object
MediaAssetReference:
description: Reference to a previously uploaded MediaAsset.
properties:
media_asset_id:
description: ID of a MediaAsset resource previously created and marked READY
title: Media Asset Id
type: string
source:
const: media_asset
default: media_asset
title: Source
type: string
required:
- media_asset_id
title: MediaAssetReference
type: object
MeshAssets:
description: Mesh asset URLs.
properties:
collider_mesh_url:
anyOf:
- type: string
- type: 'null'
description: Collider mesh URL
title: Collider Mesh Url
title: MeshAssets
type: object
MultiImagePrompt-Input:
description: 'Multi-image-to-world generation.
Generates a world from multiple images. text_prompt is optional.
Recommended image formats: jpg, jpeg, png, webp.'
properties:
disable_recaption:
anyOf:
- type: boolean
- type: 'null'
description: If True, use text_prompt as-is without recaptioning
title: Disable Recaption
multi_image_prompt:
description: List of images with optional spherical locations
items:
$ref: '#/components/schemas/SphericallyLocatedContent-Input'
title: Multi Image Prompt
type: array
reconstruct_images:
default: false
description: Whether to use reconstruction mode (allows up to 8 images,
otherwise 4)
title: Reconstruct Images
type: boolean
text_prompt:
anyOf:
- type: string
- type: 'null'
description: Optional text guidance (auto-generated if not provided)
title: Text Prompt
type:
const: multi-image
default: multi-image
title: Type
type: string
required:
- multi_image_prompt
title: MultiImagePrompt
type: object
MultiImagePrompt-Output:
description: For world models supporting multi-image (+ text) input.
properties:
multi_image_prompt:
items:
$ref: '#/components/schemas/SphericallyLocatedContent-Output'
title: Multi Image Prompt
type: array
reconstruct_images:
default: false
title: Reconstruct Images
type: boolean
text_prompt:
anyOf:
- type: string
- type: 'null'
title: Text Prompt
type:
const: multi-image
default: multi-image
title: Type
type: string
required:
- multi_image_prompt
title: MultiImagePrompt
type: object
OperationCost:
description: Settled credit cost for a successful operation.
properties:
line_items:
description: Usage events that make up the operation's total credits
items:
$ref: '#/components/schemas/OperationCostLineItem'
title: Line Items
type: array
total_credits:
description: Total credits charged for the operation
title: Total Credits
type: integer
required:
- total_credits
title: OperationCost
type: object
OperationCostLineItem:
description: A single usage event contributing to an operation's total credits.
properties:
credits:
description: Credits charged for this usage event
title: Credits
type: integer
name:
description: Human-readable usage event name
title: Name
type: string
required:
- name
- credits
title: OperationCostLineItem
type: object
OperationError:
description: Error information for a failed operation.
properties:
code:
anyOf:
- type: integer
- type: 'null'
description: Error code
title: Code
message:
anyOf:
- type: string
- type: 'null'
description: Error message
title: Message
title: OperationError
type: object
Operation_PanoDepthToRgbResult_:
properties:
cost:
anyOf:
- $ref: '#/components/schemas/OperationCost'
- type: 'null'
description: Settled credit cost for the operation. Populated only on successful
completion.
created_at:
anyOf:
- format: date-time
type: string
- type: 'null'
description: Creation timestamp
title: Created At
done:
description: True if the operation is completed
title: Done
type: boolean
error:
anyOf:
- $ref: '#/components/schemas/OperationError'
- type: 'null'
description: Error information if the operation failed
expires_at:
anyOf:
- format: date-time
type: string
- type: 'null'
description: Expiration timestamp
title: Expires At
metadata:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
description: Service-specific metadata, such as progress percentage
title: Metadata
operation_id:
description: Operation identifier
title: Operation Id
type: string
response:
anyOf:
- $ref: '#/components/schemas/PanoDepthToRgbResult'
- type: 'null'
description: Result payload when done=true and no error. Structure depends
on operation type.
updated_at:
anyOf:
- format: date-time
type: string
- type: 'null'
description: Last update timestamp
title: Updated At
required:
- operation_id
- done
title: Operation[PanoDepthToRgbResult]
type: object
PanoDepthToRgbRequest:
description: "Request to generate an RGB panorama from a depth panorama.\n\n\
Provide a depth panorama and a text prompt describing\nthe desired appearance.\
\ The depth map supplies the\nscene geometry; the model synthesizes textures\
\ that\nloosely adhere to that geometry and returns a panoramic RGB image.\n\
\nAccepted depth inputs:\n\n* **EXR**: float depth values. Omit ``z_min``\n\
\ and ``z_max``.\n* **PNG**: depth values normalized to [0, 1].\n Provide\
\ both ``z_min`` and ``z_max`` so the service\n can decode the PNG correctly.\n\
\nFor a complete PNG-based workflow, see the\n`web-chisel-depth-png example\
\ `.\n\
\nThis endpoint returns a long-running ``Operation``.\nPoll ``GET /operations/{operation_id}``\
\ until\n``done`` is ``true``, then read the generated\npanorama URL from\
\ ``response.pano_url``."
examples:
- depth_pano_image:
source: uri
uri: https://example.com/depth.exr
text_prompt: The scene is a warm, rustic cabin interior with a realistic style
and a cozy winter atmosphere. Wooden walls and exposed beams frame the room,
while a stone fireplace casts soft amber light across simple furnishings
and natural textures. The 360 scene is faultless.
- depth_pano_image:
source: uri
uri: https://example.com/greenhouse-depth.exr
text_prompt: A peaceful greenhouse workshop filled with plants and glassware,
with soft sunlight, weathered wood, and ivy creeping along the walls.
- depth_pano_image:
source: uri
uri: https://example.com/depth.png
text_prompt: The scene is a warm Mediterranean kitchen rendered in a realistic
style with a bright, welcoming coastal atmosphere. The overall tone is relaxed
and sunlit, combining natural materials with crisp handcrafted details.
Blue ceramic tiles wrap the backsplash and continue across key architectural
surfaces, giving the room a cool accent against creamy plaster walls. Wooden
cabinetry lines the perimeter in a balanced arrangement, with paneled doors,
open shelving, and brass hardware contributing to the room's refined but
lived-in character. A broad countertop of pale stone runs between the cabinets,
creating a continuous work surface that anchors the cooking area. A central
table with a solid wood top occupies the middle of the room, surrounded
by simple chairs and small ceramic vessels that reinforce the artisanal
style. Arched openings connect the kitchen to adjacent areas, while French
doors admit generous daylight and suggest an outdoor terrace nearby. Terracotta
accents, woven textures, and neatly arranged cookware add visual richness
without cluttering the space. The cabinetry, tiled surfaces, and stone counters
form a coherent ring around the room, while the table remains the central
focal element within the composition. The 360 scene is faultless.
z_max: 100.0
z_min: 0.1
properties:
depth_pano_image:
description: Depth panorama image (EXR or PNG). Provide via URI, media_asset,
or base64 (max 100 MB).
discriminator:
mapping:
data_base64: '#/components/schemas/DataBase64Reference'
media_asset: '#/components/schemas/MediaAssetReference'
uri: '#/components/schemas/UriReference'
propertyName: source
oneOf:
- $ref: '#/components/schemas/MediaAssetReference'
- $ref: '#/components/schemas/UriReference'
- $ref: '#/components/schemas/DataBase64Reference'
title: Depth Pano Image
seed:
anyOf:
- maximum: 4294967295.0
minimum: 0.0
type: integer
- type: 'null'
description: Random seed for reproducible generation.
title: Seed
text_prompt:
description: Text description of the desired appearance.
title: Text Prompt
type: string
z_max:
anyOf:
- exclusiveMinimum: 0.0
type: number
- type: 'null'
description: Maximum depth. Required for PNG depth maps (normalized to [0,
1]). Must be provided together with z_min and must be greater than z_min.
Omit for EXR depth maps.
title: Z Max
z_min:
anyOf:
- exclusiveMinimum: 0.0
type: number
- type: 'null'
description: Minimum depth. Required for PNG depth maps (normalized to [0,
1]). Must be provided together with z_max. Omit for EXR depth maps.
title: Z Min
required:
- depth_pano_image
- text_prompt
title: PanoDepthToRgbRequest
type: object
PanoDepthToRgbResult:
description: 'Result from depth-to-RGB panorama generation.
Returned inside ``Operation.response`` when the
operation completes successfully (``done=true``).'
properties:
pano_url:
anyOf:
- type: string
- type: 'null'
description: URL of the generated RGB panorama image. Available when the
operation succeeds.
title: Pano Url
title: PanoDepthToRgbResult
type: object
PanoDetectionMode:
enum:
- auto
- true
- false
Permission:
description: Access control permissions for a resource.
properties:
allow_id_access:
default: false
title: Allow Id Access
type: boolean
allowed_readers:
items:
type: string
title: Allowed Readers
type: array
allowed_writers:
items:
type: string
title: Allowed Writers
type: array
public:
default: false
title: Public
type: boolean
title: Permission
type: object
Prompt:
description: 'For world models generating a world from a single image (+ text).
Images can be generated using the :image-generation method.
If no text prompt is provided, it will be generated via recaption.'
properties:
image_prompt:
$ref: '#/components/schemas/Content'
is_pano:
default: false
title: Is Pano
type: boolean
text_prompt:
anyOf:
- type: string
- type: 'null'
title: Text Prompt
type:
const: image
default: image
title: Type
type: string
required:
- image_prompt
title: Prompt
type: object
SphericallyLocatedContent-Input:
description: Content with a preferred location on the sphere.
properties:
azimuth:
anyOf:
- type: number
- type: 'null'
description: Azimuth angle in degrees
title: Azimuth
content:
description: The content at this location
discriminator:
mapping:
data_base64: '#/components/schemas/DataBase64Reference'
media_asset: '#/components/schemas/MediaAssetReference'
uri: '#/components/schemas/UriReference'
propertyName: source
oneOf:
- $ref: '#/components/schemas/MediaAssetReference'
- $ref: '#/components/schemas/UriReference'
- $ref: '#/components/schemas/DataBase64Reference'
title: Content
required:
- content
title: SphericallyLocatedContent
type: object
SphericallyLocatedContent-Output:
description: Content with a preferred location on the sphere.
properties:
azimuth:
anyOf:
- type: number
- type: 'null'
title: Azimuth
data_base64:
anyOf:
- type: string
- type: 'null'
title: Data Base64
extension:
anyOf:
- type: string
- type: 'null'
description: File extension without dot
examples:
- jpg
- png
- pdf
- txt
title: Extension
uri:
anyOf:
- type: string
- type: 'null'
title: Uri
title: SphericallyLocatedContent
type: object
SplatAssets:
description: Gaussian splat asset URLs.
properties:
semantics_metadata:
anyOf:
- $ref: '#/components/schemas/WorldSemanticsMetadata'
- type: 'null'
description: Semantic metadata for the world
spz_urls:
anyOf:
- additionalProperties:
type: string
type: object
- type: 'null'
description: URLs for SPZ format Gaussian splat files
title: Spz Urls
title: SplatAssets
type: object
UploadUrlInfo:
description: Information required to upload raw bytes directly to storage.
properties:
curl_example:
anyOf:
- type: string
- type: 'null'
description: Optional curl example for convenience
title: Curl Example
required_headers:
anyOf:
- additionalProperties:
type: string
type: object
- type: 'null'
description: Headers that MUST be included when uploading (e.g. Content-Type)
title: Required Headers
upload_method:
description: Upload method
title: Upload Method
type: string
upload_url:
description: Signed URL for uploading bytes via PUT
title: Upload Url
type: string
required:
- upload_url
- upload_method
title: UploadUrlInfo
type: object
UriReference:
description: Reference to content via a publicly accessible URL.
properties:
source:
const: uri
default: uri
title: Source
type: string
uri:
description: Publicly accessible URL pointing to the media
title: Uri
type: string
required:
- uri
title: UriReference
type: object
ValidationError:
properties:
loc:
items:
anyOf:
- type: string
- type: integer
title: Location
type: array
msg:
title: Message
type: string
type:
title: Error Type
type: string
required:
- loc
- msg
- type
title: ValidationError
type: object
VideoPrompt-Input:
description: 'Video-to-world generation.
Generates a world from a video. text_prompt is optional.
Recommended video formats: mp4, webm, mov, avi.
Maximum video size: 100MB.'
properties:
disable_recaption:
anyOf:
- type: boolean
- type: 'null'
description: If True, use text_prompt as-is without recaptioning
title: Disable Recaption
text_prompt:
anyOf:
- type: string
- type: 'null'
description: Optional text guidance (auto-generated if not provided)
title: Text Prompt
type:
const: video
default: video
title: Type
type: string
video_prompt:
description: Video content for world generation
discriminator:
mapping:
data_base64: '#/components/schemas/DataBase64Reference'
media_asset: '#/components/schemas/MediaAssetReference'
uri: '#/components/schemas/UriReference'
propertyName: source
oneOf:
- $ref: '#/components/schemas/MediaAssetReference'
- $ref: '#/components/schemas/UriReference'
- $ref: '#/components/schemas/DataBase64Reference'
title: Video Prompt
required:
- video_prompt
title: VideoPrompt
type: object
VideoPrompt-Output:
description: For world models supporting video (+ text) input.
properties:
text_prompt:
anyOf:
- type: string
- type: 'null'
title: Text Prompt
type:
const: video
default: video
title: Type
type: string
video_prompt:
$ref: '#/components/schemas/Content'
required:
- video_prompt
title: VideoPrompt
type: object
World:
description: A generated world, including asset URLs.
properties:
assets:
anyOf:
- $ref: '#/components/schemas/WorldAssets'
- type: 'null'
description: Generated world assets
created_at:
anyOf:
- format: date-time
type: string
- type: 'null'
description: Creation timestamp
title: Created At
display_name:
description: Display name
title: Display Name
type: string
model:
anyOf:
- type: string
- type: 'null'
description: Model used for generation
title: Model
permission:
anyOf:
- $ref: '#/components/schemas/Permission'
- type: 'null'
description: Access control permissions for the world
tags:
anyOf:
- items:
type: string
type: array
- type: 'null'
description: Tags associated with the world
title: Tags
updated_at:
anyOf:
- format: date-time
type: string
- type: 'null'
description: Last update timestamp
title: Updated At
world_id:
description: World identifier
title: World Id
type: string
world_marble_url:
description: World Marble URL
title: World Marble Url
type: string
world_prompt:
anyOf:
- discriminator:
mapping:
depth-pano: '#/components/schemas/DepthPanoPrompt'
image: '#/components/schemas/Prompt'
inpaint-pano: '#/components/schemas/InpaintPanoPrompt'
multi-image: '#/components/schemas/MultiImagePrompt-Output'
text: '#/components/schemas/WorldTextPrompt-Output'
video: '#/components/schemas/VideoPrompt-Output'
propertyName: type
oneOf:
- $ref: '#/components/schemas/WorldTextPrompt-Output'
- $ref: '#/components/schemas/Prompt'
- $ref: '#/components/schemas/MultiImagePrompt-Output'
- $ref: '#/components/schemas/VideoPrompt-Output'
- $ref: '#/components/schemas/DepthPanoPrompt'
- $ref: '#/components/schemas/InpaintPanoPrompt'
- type: 'null'
description: World prompt
title: World Prompt
required:
- world_id
- display_name
- world_marble_url
title: World
type: object
WorldAssets:
description: Downloadable outputs of world generation.
properties:
caption:
anyOf:
- type: string
- type: 'null'
description: AI-generated description of the world
title: Caption
imagery:
anyOf:
- $ref: '#/components/schemas/ImageryAssets'
- type: 'null'
description: Imagery assets
mesh:
anyOf:
- $ref: '#/components/schemas/MeshAssets'
- type: 'null'
description: Mesh assets
splats:
anyOf:
- $ref: '#/components/schemas/SplatAssets'
- type: 'null'
description: Gaussian splat assets
thumbnail_url:
anyOf:
- type: string
- type: 'null'
description: Thumbnail URL for the world
title: Thumbnail Url
title: WorldAssets
type: object
WorldSemanticsMetadata:
description: "Semantic metadata describing how to interpret the generated splat\n\
asset in real-world units.\n\nThe exported asset is in arbitrary model units.\
\ Apply the transform\nbelow to convert to a metric, ground-aligned frame:\n\
\n metric_xyz = raw_xyz * metric_scale_factor\n aligned_xyz = metric_xyz\
\ - (0, ground_plane_offset, 0)\n\nBoth fields are populated on every world\
\ generated by the current\npipeline. They remain Optional only for backwards\
\ compatibility with\nworlds generated before December 2025, which predate\
\ metric-scale\nestimation.\n\nTODO: once pre-December-2025 worlds are migrated\
\ or aged out, tighten\nthese fields to non-Optional and drop the `| None`\
\ from the type."
properties:
ground_plane_offset:
anyOf:
- type: number
- type: 'null'
description: Vertical (Y-axis) offset, in meters, of the detected ground
plane in the metric-scaled frame. Subtract from Y after applying `metric_scale_factor`
to place the ground at y=0.
title: Ground Plane Offset
metric_scale_factor:
anyOf:
- type: number
- type: 'null'
description: Multiplicative factor that converts raw asset coordinates to
meters. Multiply all XYZ positions (and isotropic scales) by this value.
A value of 1.0 means scale could not be inferred and the asset was left
in model units.
title: Metric Scale Factor
title: WorldSemanticsMetadata
type: object
WorldTextPrompt-Input:
description: 'Text-to-world generation.
Generates a world from a text description. text_prompt is REQUIRED.'
properties:
disable_recaption:
anyOf:
- type: boolean
- type: 'null'
description: If True, use text_prompt as-is without recaptioning
title: Disable Recaption
text_prompt:
anyOf:
- type: string
- type: 'null'
description: Optional text guidance (auto-generated if not provided)
title: Text Prompt
type:
const: text
default: text
title: Type
type: string
title: WorldTextPrompt
type: object
WorldTextPrompt-Output:
description: Input prompt class for text-conditioned world generation.
properties:
text_prompt:
anyOf:
- type: string
- type: 'null'
title: Text Prompt
type:
const: text
default: text
title: Type
type: string
title: WorldTextPrompt
type: object
WorldsGenerateRequest:
description: Request to generate a world from text, image, multi-image, or video
input.
examples:
- display_name: Coastal Castle
model: marble-1.0
permission:
public: false
seed: 42
tags:
- fantasy
- coastal
world_prompt:
text_prompt: A grand, ivy-covered castle rises from the shoreline at sunset,
its reflection shimmering in the tranquil ocean waves.
type: text
- display_name: Greenhouse Workshop
model: marble-1.0-draft
world_prompt:
image_prompt:
source: uri
uri: https://example.com/my-image.jpg
text_prompt: A peaceful, sunlit greenhouse workshop filled with plants and
glassware, where soft light filters through cracked windows and ivy creeps
along the walls.
type: image
- display_name: World from an Existing Pano
model: marble-1.1
world_prompt:
image_prompt:
source: uri
uri: https://example.com/my-equirect-pano.jpg
is_pano: auto
type: image
- permission:
public: true
world_prompt:
type: video
video_prompt:
media_asset_id: 550e8400e29b41d4a716446655440000
source: media_asset
- display_name: World from Multiple Images
model: marble-1.1
world_prompt:
multi_image_prompt:
- azimuth: 0
content:
source: uri
uri: https://example.com/image1.jpg
- azimuth: 180
content:
source: uri
uri: https://example.com/image2.jpg
type: multi-image
properties:
display_name:
anyOf:
- maxLength: 64
type: string
- type: 'null'
description: Optional human-readable title for the world (max 64 characters).
Stored as world metadata and returned in world responses; does not affect
generation. If omitted, World Labs may generate a title from the prompt.
title: Display Name
model:
anyOf:
- enum:
- marble-1.0-draft
- marble-1.0
- marble-1.1
- marble-1.1-plus
type: string
- enum:
- Marble 0.1-mini
- Marble 0.1-plus
- Marble 1.1-plus
type: string
default: marble-1.1
description: The model to use for generation. marble-1.1-plus includes dynamic
world sizing. Legacy names ('Marble 0.1-plus', etc.) still work but are
deprecated and will be removed in a future release.
title: Model
permission:
$ref: '#/components/schemas/Permission'
default:
allow_id_access: false
allowed_readers: []
allowed_writers: []
public: false
description: The permission for the world
seed:
anyOf:
- maximum: 4294967295.0
minimum: 0.0
type: integer
- type: 'null'
description: Random seed for generation
title: Seed
tags:
anyOf:
- items:
type: string
maxItems: 10
type: array
- type: 'null'
description: Optional tags for the world (max 10 tags, each up to 32 characters)
title: Tags
world_prompt:
description: The prompt specifying how to generate the world
discriminator:
mapping:
image: '#/components/schemas/ImagePrompt'
multi-image: '#/components/schemas/MultiImagePrompt-Input'
text: '#/components/schemas/WorldTextPrompt-Input'
video: '#/components/schemas/VideoPrompt-Input'
propertyName: type
oneOf:
- $ref: '#/components/schemas/WorldTextPrompt-Input'
- $ref: '#/components/schemas/ImagePrompt'
- $ref: '#/components/schemas/MultiImagePrompt-Input'
- $ref: '#/components/schemas/VideoPrompt-Input'
title: World Prompt
required:
- world_prompt
title: WorldsGenerateRequest
type: object
securitySchemes:
ApiKeyAuth:
description: API key for authentication. Get your key from the developer portal.
in: header
name: WLT-Api-Key
type: apiKey
info:
description: Public-facing API for the Marble platform
summary: Marble Public API v1
title: Marble Public API v1
version: 1.0.0
openapi: 3.1.0
paths:
/marble/v1/credits:
get:
description: "Get remaining API credits for the authenticated user.\n\nReturns:\n\
\ CreditsResponse with the current aggregate API credit balance.\n\nRaises:\n\
\ HTTPException: 404 if the caller is not an API-enabled user\n HTTPException:\
\ 503 if the billing backend is unavailable"
operationId: get_credits_marble_v1_credits_get
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/CreditsResponse'
description: Successful Response
summary: Get Credits
tags:
- credits
/marble/v1/media-assets/{media_asset_id}:
get:
description: "Get a media asset by ID.\n\nRetrieves metadata for a previously\
\ created media asset.\n\nArgs:\n media_asset_id: The media asset identifier.\n\
\nReturns:\n MediaAsset object with media_asset_id, file_name, extension,\
\ kind,\n metadata, created_at, and updated_at.\n\nRaises:\n HTTPException:\
\ 404 if not found"
operationId: get_media_asset_marble_v1_media_assets__media_asset_id__get
parameters:
- in: path
name: media_asset_id
required: true
schema:
title: Media Asset Id
type: string
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/MediaAsset'
description: Successful Response
'422':
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
description: Validation Error
summary: Get Media Asset
/marble/v1/media-assets:prepare_upload:
post:
description: "Prepare a media asset upload for use in world generation.\n\n\
This API endpoint creates a media asset record and returns a signed upload\
\ URL.\nUse this workflow to upload images or videos that you want to reference\
\ in world\ngeneration requests.\n\n## Workflow\n\n1. **Prepare Upload** (this\
\ endpoint): Get a `media_asset_id` and `upload_url`\n2. **Upload File**:\
\ Use the signed URL to upload your file\n3. **Generate World**: Reference\
\ the `media_asset_id` in `/worlds:generate` with\n source type \"media_asset\"\
\n\n## Request Parameters\n\n- `file_name`: Your file's name (e.g., \"landscape.jpg\"\
)\n- `extension`: File extension without dot (e.g., \"jpg\", \"png\", \"mp4\"\
)\n- `kind`: Either \"image\" or \"video\"\n- `metadata`: Optional custom\
\ metadata object\n\n## Response\n\nReturns a `MediaAssetPrepareUploadResponse`\
\ containing:\n\n- `media_asset`: Object with `media_asset_id` (use this in\
\ world generation)\n- `upload_info`: Object with `upload_url`, `required_headers`,\
\ and `curl_example`\n\n## Uploading Your File\n\nUse the returned `upload_url`\
\ and `required_headers` to upload your file:\n\n```bash\ncurl --request PUT\
\ \\\n --url \\\n --header \"Content-Type: \"\
\ \\\n --header \": \" \\\n --upload-file /path/to/your/file\n\
```\n\nReplace:\n- ``: The `upload_url` from the response\n- ``:\
\ MIME type (e.g., `image/png`, `image/jpeg`, `video/mp4`)\n- `:\
\ `: Each header from `required_headers`\n- `/path/to/your/file`:\
\ Path to your local file\n\n## Example Usage in World Generation\n\nAfter\
\ uploading, use the `media_asset_id` in a world generation request:\n\n```json\n\
{\n \"world_prompt\": {\n \"type\": \"image\",\n \"image_prompt\":\
\ {\n \"source\": \"media_asset\",\n \"media_asset_id\": \"\"\
\n }\n }\n}\n```"
operationId: prepare_media_asset_upload_marble_v1_media_assets_prepare_upload_post
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/MediaAssetPrepareUploadRequest'
required: true
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/MediaAssetPrepareUploadResponse'
description: Successful Response
'422':
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
description: Validation Error
summary: Prepare a media asset upload
/marble/v1/operations/{operation_id}:
get:
description: "Get an operation by ID.\n\nPoll this endpoint to check the status\
\ of a long-running operation.\nWhen done=true, the response field contains\
\ the generated world.\n\nArgs:\n operation_id: The operation identifier\
\ from /worlds:generate.\n\nReturns:\n GetOperationResponse[World] with:\n\
\ - operation_id: Operation identifier\n - created_at: Creation\
\ timestamp\n - updated_at: Last update timestamp\n - expires_at:\
\ Expiration timestamp\n - done: true when complete, false while in\
\ progress\n - error: Error details if failed, null otherwise\n \
\ - metadata: Progress information and world_id\n - response: Generated\
\ World if done=true, null otherwise\n - cost: Settled credit cost\
\ breakdown; populated only when the\n operation completed successfully\
\ (done=true with no error)\n and a public price is known for its\
\ operation type. Failed\n or in-progress operations return null.\n\
\nRaises:\n HTTPException: 401 if unauthorized\n HTTPException: 404\
\ if operation not found\n HTTPException: 500 if request fails"
operationId: get_operation_marble_v1_operations__operation_id__get
parameters:
- in: path
name: operation_id
required: true
schema:
title: Operation Id
type: string
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/GetOperationResponse_Union_World__PanoDepthToRgbResult__'
description: Successful Response
'422':
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
description: Validation Error
summary: Get Operation
/marble/v1/pano:depth_to_rgb:
post:
description: "Generate an RGB panorama from a depth panorama.\n\nProvide a depth\
\ panorama and a text prompt\ndescribing the desired appearance. The depth\
\ map\nsupplies the scene geometry; the model synthesizes\ntextures that match\
\ that geometry and returns a\npanoramic RGB image.\n\nAccepted depth inputs:\n\
\n* **EXR**: float depth values. Omit\n ``z_min`` and ``z_max``.\n* **PNG**:\
\ depth values normalized to [0, 1].\n Provide both ``z_min`` and ``z_max``\
\ so the\n service can decode the PNG correctly.\n\nFor a complete PNG-based\
\ workflow, see the\n[web-chisel-depth-png example](https://github.com/worldlabsai/worldlabs-api-examples/tree/main/web-chisel-depth-png).\n\
\nReturns a long-running ``Operation``. Poll\n``GET /operations/{operation_id}``\
\ until\n``done`` is ``true``, then read the generated\npanorama URL from\
\ ``response.pano_url``.\n\nRaises:\n HTTPException: 400 if invalid request\n\
\ HTTPException: 402 if insufficient credits\n HTTPException: 500 if\
\ generation could not start"
operationId: pano_depth_to_rgb_marble_v1_pano_depth_to_rgb_post
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PanoDepthToRgbRequest'
required: true
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/Operation_PanoDepthToRgbResult_'
description: Successful Response
'402':
content:
application/json:
example:
detail: Insufficient API credits to start this request. Add credits
or enable auto-refill at https://platform.worldlabs.ai/billing.
description: The account has insufficient API credits for this request.
'422':
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
description: Validation Error
summary: Pano Depth To Rgb
/marble/v1/worlds/{world_id}:
delete:
description: "Delete a world by ID.\n\nPermanently deletes a world and its associated\
\ assets.\nOnly the world owner can delete a world.\n\nArgs:\n world_id:\
\ The unique identifier of the world to delete.\n\nReturns:\n DeleteWorldResponse\
\ confirming the deletion.\n\nRaises:\n HTTPException: 403 if user is not\
\ the world owner\n HTTPException: 404 if world not found\n HTTPException:\
\ 500 if deletion fails"
operationId: delete_world_marble_v1_worlds__world_id__delete
parameters:
- in: path
name: world_id
required: true
schema:
title: World Id
type: string
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/DeleteWorldResponse'
description: Successful Response
'422':
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
description: Validation Error
summary: Delete World
get:
description: "Get a world by ID.\n\nRetrieves a world's details including generated\
\ assets if available.\nOnly the world owner or users with access to public\
\ worlds can retrieve them.\n\nArgs:\n world_id: The unique identifier\
\ of the world.\n\nReturns:\n World object with world_id, display_name,\
\ tags, assets, created_at,\n updated_at, permission, model, world_prompt,\
\ and world_marble_url.\n\nRaises:\n HTTPException: 404 if world not found\
\ or access denied"
operationId: get_world_marble_v1_worlds__world_id__get
parameters:
- in: path
name: world_id
required: true
schema:
title: World Id
type: string
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/World'
description: Successful Response
'422':
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
description: Validation Error
summary: Get World
/marble/v1/worlds:generate:
post:
description: "Start world generation.\n\nCreates a new world generation job\
\ and returns a long-running operation.\nPoll the /operations/{operation_id}\
\ endpoint to check generation status\nand retrieve the generated world when\
\ complete.\n\nArgs:\n request: The world generation request containing\
\ world_prompt, display_name,\n tags, model, seed, and permission settings.\n\
\nReturns:\n GenerateWorldResponse with operation_id and timestamps. Use\
\ the operation_id\n to poll for completion.\n\nRaises:\n HTTPException:\
\ 400 if invalid request or content violates policies\n HTTPException:\
\ 402 if insufficient credits\n HTTPException: 500 if generation could\
\ not be started"
operationId: generate_world_marble_v1_worlds_generate_post
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/WorldsGenerateRequest'
required: true
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/GenerateWorldResponse'
description: Successful Response
'402':
content:
application/json:
example:
detail: Insufficient API credits to start this request. Add credits
or enable auto-refill at https://platform.worldlabs.ai/billing.
description: The account has insufficient API credits for this request.
'422':
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
description: Validation Error
summary: Generate World
/marble/v1/worlds:list:
post:
description: "List worlds with optional filters.\n\nReturns worlds created through\
\ the API with optional filtering and pagination.\n\nArgs:\n request: List\
\ request with optional filters:\n - page_size: Number of results per\
\ page (default: 10)\n - page_token: Pagination token from previous\
\ response\n - status: Filter by status (e.g., \"COMPLETED\")\n \
\ - model: Filter by model name (e.g., \"marble-1.0-plus\")\n -\
\ tags: Filter by tags (matches worlds with any tag)\n - is_public:\
\ Filter by visibility (true=public, false=private, null=all)\n - created_after:\
\ Filter by creation time (after timestamp)\n - created_before: Filter\
\ by creation time (before timestamp)\n - sort_by: Sort order (\"created_at\"\
\ or \"updated_at\")\n\nReturns:\n ListWorldsResponse with worlds list\
\ and next_page_token for pagination.\n\nRaises:\n HTTPException: 400 if\
\ invalid parameters\n HTTPException: 500 if request fails"
operationId: list_worlds_marble_v1_worlds_list_post
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ListWorldsRequest'
required: true
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/ListWorldsResponse'
description: Successful Response
'422':
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
description: Validation Error
summary: List Worlds
security:
- ApiKeyAuth: []
servers:
- description: World API
url: https://api.worldlabs.ai
````
# Get an operation
Source: https://docs.worldlabs.ai/api/reference/operations/get
GET /marble/v1/operations/{operation_id}
Get an operation by ID.
Poll this endpoint to check the status of a long-running operation.
When done=true, the response field contains the generated world.
Args:
operation_id: The operation identifier from /worlds:generate.
Returns:
GetOperationResponse[World] with:
- operation_id: Operation identifier
- created_at: Creation timestamp
- updated_at: Last update timestamp
- expires_at: Expiration timestamp
- done: true when complete, false while in progress
- error: Error details if failed, null otherwise
- metadata: Progress information and world_id
- response: Generated World if done=true, null otherwise
- cost: Settled credit cost breakdown; populated only when the
operation completed successfully (done=true with no error)
and a public price is known for its operation type. Failed
or in-progress operations return null.
Raises:
HTTPException: 401 if unauthorized
HTTPException: 404 if operation not found
HTTPException: 500 if request fails
# Operations
Source: https://docs.worldlabs.ai/api/reference/operations/index
Poll long-running API jobs until they complete.
Long-running endpoints return an `operation_id`. Use the operations endpoint to
check status, inspect progress metadata, and retrieve the final response.
**GET** `/marble/v1/operations/{operation_id}`
Poll a world-generation or pano operation until `done` is `true`.
## Response states
* `done: false`: The job is still running. Check `metadata` for progress details
when present.
* `done: true` with `error: null`: The job completed. Read `response` for the
generated result.
* `done: true` with `error`: The job failed. Use the error code and message to
decide whether to retry or adjust the request.
# Depth to RGB
Source: https://docs.worldlabs.ai/api/reference/pano/depth_to_rgb
POST /marble/v1/pano:depth_to_rgb
Generate an RGB panorama from a depth panorama.
Provide a depth panorama and a text prompt
describing the desired appearance. The depth map
supplies the scene geometry; the model synthesizes
textures that match that geometry and returns a
panoramic RGB image.
Accepted depth inputs:
* **EXR**: float depth values. Omit
``z_min`` and ``z_max``.
* **PNG**: depth values normalized to [0, 1].
Provide both ``z_min`` and ``z_max`` so the
service can decode the PNG correctly.
For a complete PNG-based workflow, see the
[web-chisel-depth-png example](https://github.com/worldlabsai/worldlabs-api-examples/tree/main/web-chisel-depth-png).
Returns a long-running ``Operation``. Poll
``GET /operations/{operation_id}`` until
``done`` is ``true``, then read the generated
panorama URL from ``response.pano_url``.
Raises:
HTTPException: 400 if invalid request
HTTPException: 402 if insufficient credits
HTTPException: 500 if generation could not start
`depth_pano_image` must be a full 360° equirectangular depth panorama with a
2:1 width-to-height aspect ratio. Inputs that are not 2:1 are center-cropped to
that ratio before generation, which discards content at the edges.
# Pano
Source: https://docs.worldlabs.ai/api/reference/pano/index
Generate RGB panoramas from depth panorama inputs.
Pano endpoints work with panorama assets and return long-running operations.
**POST** `/marble/v1/pano:depth_to_rgb`
Generate an RGB panorama from a depth panorama and a text description.
## Flow
The endpoint returns an operation. Poll
[Get an operation](/api/reference/operations/get) until the generated panorama
URL is available in the response.
# Delete a world
Source: https://docs.worldlabs.ai/api/reference/worlds/delete
DELETE /marble/v1/worlds/{world_id}
Delete a world by ID.
Permanently deletes a world and its associated assets.
Only the world owner can delete a world.
Args:
world_id: The unique identifier of the world to delete.
Returns:
DeleteWorldResponse confirming the deletion.
Raises:
HTTPException: 403 if user is not the world owner
HTTPException: 404 if world not found
HTTPException: 500 if deletion fails
# Export a world
Source: https://docs.worldlabs.ai/api/reference/worlds/export
POST /marble/v1/worlds/{world_id}:export
Export a generated world asset.
PLY splat exports are converted synchronously, cached in GCS, and returned
as completed operations. HQ mesh exports reuse the existing async mesh
export service and return an in-progress operation.
# Generate a world
Source: https://docs.worldlabs.ai/api/reference/worlds/generate
POST /marble/v1/worlds:generate
Start world generation.
Creates a new world generation job and returns a long-running operation.
Poll the /operations/{operation_id} endpoint to check generation status
and retrieve the generated world when complete.
Args:
request: The world generation request containing world_prompt, display_name,
tags, model, seed, and permission settings.
Returns:
GenerateWorldResponse with operation_id and timestamps. Use the operation_id
to poll for completion.
Raises:
HTTPException: 400 if invalid request or content violates policies
HTTPException: 402 if insufficient credits
HTTPException: 500 if generation could not be started
# Get a world
Source: https://docs.worldlabs.ai/api/reference/worlds/get
GET /marble/v1/worlds/{world_id}
Get a world by ID.
Retrieves a world's details including generated assets if available.
Only the world owner or users with access to public worlds can retrieve them.
Args:
world_id: The unique identifier of the world.
Returns:
World object with world_id, display_name, tags, assets, created_at,
updated_at, permission, model, world_prompt, and world_marble_url.
Raises:
HTTPException: 404 if world not found or access denied
# Worlds
Source: https://docs.worldlabs.ai/api/reference/worlds/index
Create worlds and manage API-generated world resources.
World endpoints start generation jobs and manage worlds created through the API.
World generation returns an operation. Poll the
[operations endpoint](/api/reference/operations/get) until `done` is `true`,
then read the generated world from the operation response or fetch it by ID.
**POST** `/marble/v1/worlds:generate`
Start a world generation job.
**POST** `/marble/v1/worlds:list`
List API-generated worlds.
**GET** `/marble/v1/worlds/{world_id}`
Retrieve a world, including generated asset URLs when they are available.
**POST** `/marble/v1/worlds/{world_id}:export`
Download PLY splats or start a high-quality mesh export.
**DELETE** `/marble/v1/worlds/{world_id}`
Permanently delete a world and its associated assets.
## Generation flow
For local image or video files, first create a
[media asset upload](/api/reference/media-assets/prepare-upload).
Call [Generate a world](/api/reference/worlds/generate). Save the returned
`operation_id`.
Call [Get an operation](/api/reference/operations/get) until the operation
completes.
Read the world response for Marble links, thumbnails, panorama imagery,
Gaussian splat files, and mesh assets. Use
[Export a world](/api/reference/worlds/export) for cached `.ply` splat
downloads or high-quality mesh exports. If you render `.spz` files in a
third-party engine, see
[Rendering Marble SPZ files in third-party engines](/api/rendering-spz).
# List worlds
Source: https://docs.worldlabs.ai/api/reference/worlds/list
POST /marble/v1/worlds:list
List worlds with optional filters.
Returns worlds created through the API with optional filtering and pagination.
Args:
request: List request with optional filters:
- page_size: Number of results per page (default: 10)
- page_token: Pagination token from previous response
- status: Filter by status (e.g., "COMPLETED")
- model: Filter by model name (e.g., "marble-1.0-plus")
- tags: Filter by tags (matches worlds with any tag)
- is_public: Filter by visibility (true=public, false=private, null=all)
- created_after: Filter by creation time (after timestamp)
- created_before: Filter by creation time (before timestamp)
- sort_by: Sort order ("created_at" or "updated_at")
Returns:
ListWorldsResponse with worlds list and next_page_token for pagination.
Raises:
HTTPException: 400 if invalid parameters
HTTPException: 500 if request fails
# Rendering Marble SPZ files in third-party engines
Source: https://docs.worldlabs.ai/api/rendering-spz
Apply Marble splat scale metadata when rendering SPZ assets outside Marble.
Marble SPZ files are exported in the generated asset coordinate frame. When you
render them outside Marble, use `assets.splats.semantics_metadata` to convert
Gaussian centers and Gaussian sizes into metric, ground-aligned coordinates.
## Read the scale metadata
Completed world responses include SPZ URLs and scale metadata together:
```json theme={null}
{
"assets": {
"splats": {
"spz_urls": {
"500k": "<500k_spz_url>",
"100k": "<100k_spz_url>",
"full_res": ""
},
"semantics_metadata": {
"metric_scale_factor": 1.23,
"ground_plane_offset": 0.42
}
}
}
}
```
The numeric values in this example are representative, not fixed constants. Use
the values returned with the world you are rendering.
`metric_scale_factor` converts raw generated asset units to meters.
`ground_plane_offset` places the metric ground plane at `y = 0`.
## Apply center and size scale
Apply scale to Gaussian centers and Gaussian sizes:
```python theme={null}
def to_metric_gaussian(center, linear_scale, semantics):
scale = semantics["metric_scale_factor"]
metric_center = center * scale
metric_center[..., 1] -= semantics["ground_plane_offset"]
metric_linear_scale = linear_scale * scale
return metric_center, metric_linear_scale
```
The ground-plane offset applies only to Gaussian centers. Do not apply
`ground_plane_offset` to Gaussian sizes.
## Handle log-scale fields
Some Gaussian splat formats expose `scale_0`, `scale_1`, and `scale_2` as
log-space values. If your decoder gives you log scales, add the logarithm of the
metric scale instead of multiplying the log values directly:
```python theme={null}
import math
def to_metric_log_scale(log_scale, semantics):
return log_scale + math.log(semantics["metric_scale_factor"])
```
Equivalently, convert to linear scale first:
```python theme={null}
import numpy as np
linear_scale = np.exp(log_scale)
metric_linear_scale = linear_scale * semantics["metric_scale_factor"]
metric_log_scale = np.log(metric_linear_scale)
```
Multiplying `scale_0`, `scale_1`, and `scale_2` directly is only correct if
your decoder exposes those fields as linear sizes.
## Convert renderer axes separately
`semantics_metadata` converts raw Marble SPZ coordinates to a metric,
ground-aligned frame. It does not include renderer-specific axis conversion.
Generated Marble SPZ assets use the `marble_raw_opencv` convention. If your
engine uses an OpenGL or Three.js-style frame, apply your engine's axis
conversion after applying metric scale and ground alignment. Marble's web
viewer applies a 180 degree rotation around the X axis for generated SPZ assets.
# World generation examples
Source: https://docs.worldlabs.ai/api/world-generation-examples
Example-based request patterns for generating worlds from text, images, panoramas, multiple images, and video.
Use these examples when you already know the kind of input you want to send.
For the full request schema, see [`POST /marble/v1/worlds:generate`](/api/reference/worlds/generate).
## Text prompt
Text prompts are the smallest request. The API generates the panorama first,
then generates the world from that panorama.
```bash theme={null}
curl -X POST 'https://api.worldlabs.ai/marble/v1/worlds:generate' \
-H 'Content-Type: application/json' \
-H 'WLT-Api-Key: YOUR_API_KEY' \
-d '{
"display_name": "Sunlit Atrium",
"model": "marble-1.1",
"world_prompt": {
"type": "text",
"text_prompt": "A sunlit indoor atrium with marble floors, plants, and tall glass windows"
}
}'
```
## Single image
For a regular photo or illustration, omit `is_pano` or set it to `false`.
The API uses the image to generate a panorama, then generates the world.
```bash theme={null}
curl -X POST 'https://api.worldlabs.ai/marble/v1/worlds:generate' \
-H 'Content-Type: application/json' \
-H 'WLT-Api-Key: YOUR_API_KEY' \
-d '{
"display_name": "Greenhouse Workshop",
"model": "marble-1.1",
"world_prompt": {
"type": "image",
"image_prompt": {
"source": "uri",
"uri": "https://example.com/greenhouse.jpg"
},
"text_prompt": "A peaceful greenhouse workshop with warm light and dense plants"
}
}'
```
A `source: "uri"` URL must be fetchable by World Labs servers with no cookies,
authentication, or referer. Hotlink-protected or login-gated hosts can block
these fetches and return a `400`. If a URL won't fetch, upload the file as a
media asset or inline it with `source: "data_base64"`. See
[Errors and troubleshooting](/api/errors).
## Existing panorama
If your image is a full 360-degree equirectangular panorama, send it as an
image prompt. `is_pano` defaults to `auto`, so the API checks whether the image
looks like a valid equirectangular panorama before skipping pano generation.
```bash theme={null}
curl -X POST 'https://api.worldlabs.ai/marble/v1/worlds:generate' \
-H 'Content-Type: application/json' \
-H 'WLT-Api-Key: YOUR_API_KEY' \
-d '{
"display_name": "Captured Apartment Pano",
"model": "marble-1.1",
"world_prompt": {
"type": "image",
"image_prompt": {
"source": "uri",
"uri": "https://example.com/apartment-equirect-pano.jpg"
},
"is_pano": "auto"
}
}'
```
Set `is_pano` to `true` when your integration has already validated the image
as a pano. Set it to `false` for wide 2:1 images that are not 360-degree
equirectangular panoramas, such as banners, crops, or collages.
## Multiple images
Use multi-image input when you have several views of the same scene. Add
`azimuth` values when you know where each image faces.
```bash theme={null}
curl -X POST 'https://api.worldlabs.ai/marble/v1/worlds:generate' \
-H 'Content-Type: application/json' \
-H 'WLT-Api-Key: YOUR_API_KEY' \
-d '{
"display_name": "Living Room From Two Views",
"model": "marble-1.1",
"world_prompt": {
"type": "multi-image",
"multi_image_prompt": [
{
"azimuth": 0,
"content": {
"source": "uri",
"uri": "https://example.com/living-room-front.jpg"
}
},
{
"azimuth": 180,
"content": {
"source": "uri",
"uri": "https://example.com/living-room-back.jpg"
}
}
],
"text_prompt": "A cozy living room with a fireplace and bookshelves"
}
}'
```
## Local media
For local files, create a media asset, upload to the signed URL, then reference
the returned `media_asset_id` in `world_prompt`. The same flow works for local
images, local panoramas, and local videos.
```json theme={null}
{
"display_name": "Uploaded Pano",
"model": "marble-1.1",
"world_prompt": {
"type": "image",
"image_prompt": {
"source": "media_asset",
"media_asset_id": "550e8400-e29b-41d4-a716-446655440000"
},
"is_pano": "auto"
}
}
```
See the [Quickstart upload flow](/api#image-input) for the signed upload steps.
# Welcome to Marble
Source: https://docs.worldlabs.ai/index
Marble helps you create high-fidelity, persistent 3D worlds.
Marble is the first product from World Labs and is powered by our multimodal world models that can reconstruct, generate, and simulate 3D worlds. Marble lets anyone create high-fidelity, persistent 3D worlds from a single text prompt, single or multiple image, video, and coarse 3D structures.
This guide provides an overview of the Marble interface and the library of guides, tutorials, and templates for creating a world. Our goal is to help you jumpstart the world creation process so that you can publish and share your worlds with the world.
## Navigating Marble
Marble's interface is organized into several main sections to streamline your world creation workflow:
* **Gallery** - Browse and explore worlds created by the community, discover inspiration, and access your own created worlds
* **Create** - The main workspace for generating new 3D worlds using various input methods like text, images, videos, or 3D structures
* **Studio** - Advanced tools for editing, composing multiple worlds together, and creating cinematic recordings of your environments
## Creating a world
Marble offers multiple ways to create 3D worlds, each tailored to different creative workflows and input types:
### Preset
Browse and select curated preset examples to quickly generate worlds based on popular themes and styles, or roll for marbles to select a random one.
### Text prompt
Create worlds from natural language descriptions. Simply describe your vision and let Marble's AI generate a complete 3D environment.
### Single image
Transform any photograph or artwork into an immersive 3D world. Perfect for bringing 2D concepts into explorable 3D spaces. [Explore image prompt techniques →](/marble/create/prompt-guides/image-prompt)
### Multiple images
Combine multiple images to specify more visual details in the world. You can specify directional positioning of each image (Front, Back, Left, Right) or use Auto Layout [Discover multi-image creation →](/marble/create/prompt-guides/multi-image-prompt)
### Panorama
Upload 360° panoramic images for maximum control over world layout and the most accurate spatial representation. [Learn panoramic world creation →](/marble/create/prompt-guides/pano-prompt)
### Video
Upload short video clips (under 100MB) to provide rich spatial information. Ideal for capturing 360° rotational views of spaces. [Master video-based world creation →](/marble/create/prompt-guides/video-prompt)
### 3D Structure ("Chisel")
Use Marble's built-in 3D modeling tools to block out geometric layouts and architectural structures as the foundation for detailed world generation. [Get started with Chisel →](/marble/create/chisel-tools/chisel-basics)
### Reuse prompt
Quickly iterate on existing worlds by reusing successful prompts and modifying them for new variations.
## Editing a world
Enhance and modify your created worlds using Marble's powerful editing capabilities:
### Pano edit
Edit your worlds through their panoramic representation. Select specific areas and describe changes using natural language prompts to make targeted modifications while preserving the overall environment. [Learn pano editing techniques →](/marble/edit/pano-edit)
### Click and expand
Grow your worlds beyond their original boundaries by clicking on unexplored areas and generating seamless extensions that naturally connect to existing content. [Master world expansion →](/marble/create/prompt-guides/expand)
### Variation
Generate alternative versions of your worlds while maintaining core elements and style, perfect for exploring different possibilities from the same starting point. [Explore variation techniques →](/marble/edit/variations)
## Studio tools
Take your world creation to the next level with advanced studio capabilities:
### Compose
Connect and arrange multiple existing worlds into larger, seamless environments. Perfect for creating game maps, architectural complexes, or expansive connected experiences. [Learn world composition →](/marble/create/studio-tools/compose)
### Record
Create cinematic camera animations and record smooth flythrough videos of your worlds. Ideal for showcasing environments, creating trailers, or producing professional presentations. [Master animation recording →](/marble/create/studio-tools/record)
## Exporting a world
Share and use your created worlds across different platforms and applications:
### Download options
Access various export formats depending on your intended use:
* **Web sharing** - Copy shareable links for browser-based viewing and exploration
* **VR experience** - Generate VR-compatible links for immersive virtual reality viewing
* **Development assets** - Export 3D models and textures for use in game engines and development tools
* **DCC integration** - Download files compatible with digital content creation software like Blender, Maya, and 3ds Max
* **Mesh export** - Export clean 3D geometry for 3D printing, CAD software, or further modeling work
[Explore all export options →](/marble/export/gaussian-splat)
## Platform compatibility
Marble is available on the web for both desktop and mobile. Some features are not yet supported on mobile (e.g., advanced creation flow with editing tools, creating from 3D structures, pano viewing), so we recommend using Marble on desktop for the full experience.
## Generation times
We're constantly working to make it faster to generate worlds in Marble. Current estimated generation times:
* **Create pano from text, image, or 3D structure**: \~30 sec
* **Create pano from multi-image or video**: \~2 min
* **Create draft (from any input)**: \~20 sec
* **Create world (from any input)**: \~5 min
* **Expand world**: \~5 min
* **Edit pano**: \~20 sec
* **Generate high-quality mesh**: \~1 hr
## Getting help
### Frequently asked questions
Find answers to common questions about credits, file formats, sharing, VR access, and more. [Browse the FAQ →](/marble/support/faq)
### Billing and support
Learn about account management, billing information, and how to get additional support. [Visit billing and support →](/marble/support/account-billing)
Ready to start creating? Head to the [Create](/marble/create/prompt-guides) section to begin your first world, or explore the [Gallery](https://marble.worldlabs.ai) to see what's possible with Marble.
# Chisel basics
Source: https://docs.worldlabs.ai/marble/create/chisel-tools/chisel-basics
Create detailed 3D worlds from coarse 3D blocking.
# Chisel scene: 3D world blocking
Use the Chisel scene to quickly block out 3D spaces and create the foundation for detailed worlds. This tool lets you build geometric layouts that serve as the base structure for your generated environments.
## Getting started with a chisel scene
Enter Chisel from the omnibox, select 3D input mode, and enter Chisel by Start.
The Chisel scene interface provides essential tools for 3D world creation:
* **3D viewport**: The main canvas where you build your world geometry
* **Geometry panel**: Access tools like Walls and Panorama Camera
* **Template options**: Upload GLB or FBX models to start from existing geometry
* **Generation controls**: Text prompt input and generate button
## How to block out walls for a room
To create a basic room structure:
1. **Start with the wall tool**: In the Geometry panel, select **Walls**
2. **Draw wall boundaries**: Click and drag in the 3D viewport to define wall perimeters
3. **Close the room**: Connect your final wall segment back to the starting point
4. **Adjust height**: Use the wall handles to modify wall height as needed
5. **Add doorways**: Create openings by selecting wall segments and adjusting them
## How to set up camera views
Position your viewpoint for world generation:
1. **Select panorama camera**: Click on **Panorama Camera** in the Geometry panel
2. **Position the camera**: Place it where you want the generated view to originate
3. **Adjust height**: Drag the camera vertically to set the viewing height
4. **Orient the view**: Rotate the camera to face the desired direction
## How to upload reference geometry
Start with existing 3D models:
1. **Click upload**: Select **Upload a glb or fbx model** in the template section
2. **Choose your file**: Browse and select your 3D model file
3. **Position the model**: The uploaded geometry appears in the viewport
4. **Scale if needed**: Adjust the model size using the transformation handles
## How to generate your world
Transform your blocked-out geometry into a detailed environment:
1. **Add a text prompt**: In the text input, describe your desired environment (e.g., "modern kitchen")
2. **Click generate**: Press the **Generate** button to create your world
3. **Wait for processing**: The system will generate detailed geometry based on your blocks and prompt
## FAQ
### What is the Chisel ool?
The **Chisel** tool lets you modify and refine existing geometry by carving, extruding, or reshaping elements.
### What does the Extrude Tool do?
The extrude tool (Z key) extends selected surfaces outward or inward, allowing you to create depth and volume from flat shapes.
### What is the wall tool used for?
The wall tool (X key) specifically creates vertical wall segments, perfect for defining room boundaries and architectural elements.
### How do I delete geometry?
Use the delete tool () to remove selected elements from your scene. You can also press the delete key after selecting objects.
### What does the undo function do?
**Undo** (⌘Z) reverses your last action, letting you step back through your modeling history if you make mistakes.
### When should I use public mode?
Enable public mode when you want your created world to be visible to other users in the community gallery. Leave it disabled for private projects.
### What file formats can I upload?
The template uploader supports **GLB** and **FBX** file formats for 3D models. These are common formats exported from most 3D modeling software.
# Overview
Source: https://docs.worldlabs.ai/marble/create/index
Ways to generate new worlds with prompts, images, video, and Chisel
Pick the best way to begin imagining your world:
Learn how to create your first world by prompting Marble with text, images, or video.
Use Marble's built-in 3D modeling tools to block out geometric layouts and architectural structures as the foundation for detailed world generation.
Take your world creation to the next level with advanced studio capabilities: compose and arrange multiple existing worlds into larger, seamless environments, or create cinematic camera animations and record smooth flythrough videos of your worlds.
# Create & edit
Source: https://docs.worldlabs.ai/marble/create/prompt-guides/create-and-edit
Multi-stage advanced workflow for pixel-perfect world creation
The *Create & edit* advanced workflow in Marble gives you precise control over your world generation through a multi-stage process. Instead of jumping directly to a final world, you can refine and edit at each stage to ensure the perfect result.
## Multi-stage creation process
Start by dragging in your prompt into the omnibox under the 2D input mode. Check the "use advanced editing" box, and click on Create to jump into the *Create & edit* workflow.
The *Create & edit* workflow breaks world creation into distinct stages, each offering opportunities for refinement:
### Stage 1: Pano
* **Purpose**: Establish the initial panoramic representation of your world
* **Input**: Your original prompt (text, image, video, etc.)
* **Output**: A 360° panoramic view that captures the spatial layout and visual elements
### Stage 2: Panorama edit (optional)
* **Purpose**: Refine specific areas of the panorama before world generation
* **Process**:
* Directly describe changes in the prompt box, and Apply edit for global changes, or
* Select edit area in the panoramic view and describe desired changes in the prompt box and Apply edit for targeted local changes, or
* Add images to add image references for the edit, and describe desired changes in the prompt box and Apply edit for adding ingredients into the panorama.
* **Use cases**:
* Adjust lighting or colors
* Modify objects
* Add or remove details
* Fix any issues with the initial panoramic generation
* **Tips**:
* You can Queue Draft creation in the background by clicking on each panorama thumbnail. You will get a notification when it finishes, and you can find the results in your "Worlds" tab from the side-nav.
### Stage 3: Draft
* **Purpose**: Generate a quick 3D preview to evaluate spatial structure
* **Output**: A lightweight 3D world for rapid assessment
* **Benefits**: Preview the 3D structure and identify any needed changes before full processing. If any details stand out, you can further edit the panorama to change it.
### Stage 4: World generation
* **Purpose**: Create the final high-quality 3D world
* **Process**: Full processing of the panorama into a complete 3D environment
* **Output**: Complete navigable 3D world ready for exploration and export
* **Tips**:
* Toggle "Public mode" to change if generated world will be visible from public gallery.
## Additional tips
* Jump back into the *Create & edit* flow with Continue creating.
## When to use *Create & edit*
The *Create & edit* workflow is ideal when you need:
* **Precision control**: Fine-tune specific elements before committing to full world generation
* **Professional projects**: Commercial work requiring precise results and iterative feedback
* **Learning and experimentation**: Understanding how changes at different stages affect the final output
The *Create & edit* workflow transforms world generation from a single-step process into a refined, iterative experience that puts creative control in your hands.
# Expand
Source: https://docs.worldlabs.ai/marble/create/prompt-guides/expand
Expand: Improving low fidelity regions of your world.
Use Expand to extend your existing worlds beyond their current boundaries.
This tool lets you seamlessly grow your environments by clicking on unexplored areas and
generating new content that naturally connects to your existing world.
## How to access Expand
For any generated world, hit the **Continue creating** paintbrush icon within the viewer or within your worlds tab to access the Expand tool.
## Getting started with Expand
1. **Navigate your world**: Move around using standard controls to explore your current boundaries
2. **Look for edges**: Find areas where your world meets unexplored space
3. **Check for warnings**: The system shows "Not within recommended area" when you're near expansion zones
4. **Position yourself**: Move to a good viewpoint of the area you want to expand
5. **Click expand**: Press the **Expand** button in the bottom panel
You cannot expand a world generated with Marble 1.1 Plus.
## How to plan strategic expansions
## Advanced expansion techniques
### Strategic positioning
* **Doorway extensions**: Expand beyond doorways to create connected rooms
* **Corner expansions**: Expand from corners to see large new areas
* **Landscape extensions**: Extend outdoor areas to create larger environments
## Common expansion scenarios
### Indoor extensions
* Expand bedrooms into en-suite bathrooms
* Extend kitchens into dining areas or pantries
* Add hallways connecting separate rooms
* Create balconies or terraces from indoor spaces
### Outdoor growth
* Expand gardens into larger landscaped areas
* Extend courtyards into street views or neighboring buildings
* Add pathways leading to new outdoor zones
* Create transitions from indoor to outdoor spaces
### Architectural additions
* Add wings to buildings or structures
* Extend rooflines or architectural features
* Create connecting bridges or walkways
* Add levels or floors to existing structures
## FAQ
### What does "Not within recommended area" mean?
This warning appears when you're either too close to existing content or beyond the limits for expansion. It helps you identify where new content can be generated.
### How do I know where I can expand?
Look for areas where your world meets unexplored or blurry spaces. These boundary zones are where expansion is possible.
### Will new areas match my existing world's style?
Yes, the expansion system analyzes your existing world's visual style, architecture,
and aesthetic to generate new areas that naturally fit and connect with your current environment.
### Can I expand in multiple directions?
Currently, you can perform one expansion of your world in different directions. You can
export the results to studio for stitching.
### What happens if I don't like an expansion?
Each expansion creates a new version while preserving previous states. You can return
to earlier versions or try expanding in different directions from your original world.
### How do expansions connect to existing areas?
The system automatically creates seamless transitions between your existing world and
new areas, ensuring proper lighting, scale, and architectural continuity at the
connection points.
### Can I expand upwards or downwards?
Not at the moment.
# Image prompt tips
Source: https://docs.worldlabs.ai/marble/create/prompt-guides/image-prompt
How to create sharp scenes with image prompt
# Creating worlds from images
Transform your photos and images into immersive 3D worlds with Marble's image-to-world generation. Learn how to select the best images and optimize your prompts for stunning results.
## Getting started with image prompts
Image prompting in Marble works by analyzing your uploaded image and generating a 3D environment based on its content, style, and composition. The AI understands spatial relationships, lighting, and architectural elements to create explorable worlds. You can do so dragging in images to the omnibox; it will expand with 2D input mode, which allows you to start a generation by clicking on Create. For image file specifications, see [Prompt guidelines →](/marble/create/prompt-guides)
## Choosing the right images
### Image quality guidelines
**Resolution and clarity**
* Use high-resolution images (minimum 1024x1024 pixels)
* Ensure good lighting and clear details
* Avoid heavily compressed or pixelated images
* Sharp focus is preferred over blurry images
Images need to be under 20Mb
**Composition tips**
* Images with clear depth and perspective work best
* Multiple spatial elements create richer worlds
* Good balance between foreground, midground, and background
* Avoid flat or purely decorative images
## Optimizing your image selection
### What works well
* **Clear spatial definition**: Images where you can see floors, walls, ceilings, or ground planes
* **Multiple elements**: Scenes with furniture, objects, or architectural details
* **Good lighting**: Natural or artificial lighting that defines the space
### What to avoid
* **Close-up shots**: Extreme close-ups of objects without spatial context
* **Characters**: Human and animals are not well supported by the model yet.
* **Blurry images**: Blurry images result in ambiguous 3D interpretations.
* **Abstract images**: Pure abstractions without recognizable spatial elements
* **Flat graphics**: Logos, text, or 2D graphics without depth
* **Poor lighting**: Very dark, overexposed, or unclear images
* **Images with border**: Crop your image carefully to avoid flat patches of image border showing up in the 3D world.
Remember that Marble's AI interprets your image creatively, so the generated world may expand beyond what's visible in your original image, creating a fuller, explorable environment.
# Prompt guidelines
Source: https://docs.worldlabs.ai/marble/create/prompt-guides/index
Input requirements and specifications for Marble world generation
## Text prompts
* Up to 2,000 characters
* Describe a location, such as "A warm, rustic cabin living room with a glowing stone fireplace, cozy leather sofa, wooden beams, and large windows overlooking a snowy forest."
## Images
* Recommended resolution: 1024 on the long side
* Recommended aspect ratio: 16:9, 9:16, or anything in between
* Max file size: 20 MB
* Supported formats: png (recommended), jpg, webp
* See [Image prompt guide](/marble/create/prompt-guides/image-prompt) and [Multi-image prompt guide](/marble/create/prompt-guides/multi-image-prompt).
## Panoramas
* full 360 degree equirectangular projection, in 2:1 aspect ratio.
* Recommended resolution: 2560 pixels wide
* See [Panorama prompt guide](/marble/create/prompt-guides/pano-prompt).
## Video
* Max file size: 100 MB
* Max duration: 30 seconds
* Supported formats: mp4, mov, webm
* See [Video prompt guide](/marble/create/prompt-guides/video-prompt).
## 3D structure uploads ("Chisel" import)
* Max file size: 100 MB
* Supported formats: glb, fbx
# Multi-image prompt tips
Source: https://docs.worldlabs.ai/marble/create/prompt-guides/multi-image-prompt
Specify more details by combining multiple images.
# Creating worlds with multiple images with direction control
In the 2D input mode of omnibox, drag in or upload up to 4 images.
Click on the text overlay on image thumbnails to change its direction, and choose one from "Front", "Back", "Left", "Right".
For this mode, images without overlap allow the marble models to creatively fill in the spaces between views.
When you are done specifying the direction of each image, click on Create to generate the world.
For image file specifications, see [Prompt guidelines →](/marble/create/prompt-guides)
* Direction control is great for connecting different environments creatively.
# Creating worlds with multiple images with Auto Layout
In the 2D input mode of omnibox, drag in or upload up to 8 images.
Toggle "Auto Layout" switch to on so the world model automatically determines the relative positioning of these images.
In this mode, all uploaded images need to share the same aspect ratio and resolution, and should be images from the same space.
Currently images captured in close proximity of each other but covering different viewing directions, and with some overlap between images, work the best.
Click on Create to generate the world.
* Auto layout is great for quick reconstruction of existing spaces.
Auto Layout works on standard flat images and reconstructs a single space. It does not accept 360° panoramas, and you can upload only one panorama per world (a panorama can't be combined with other images or a video). To build a larger multi-room space such as a whole house, generate a separate world from each panorama or image set (one per room or vantage point), then combine them with [Studio Compose](/marble/create/studio-tools/compose). See the [Panorama prompt guide](/marble/create/prompt-guides/pano-prompt) for single-panorama capture.
## What Auto Layout can reconstruct
Auto Layout rebuilds one coherent space from what your images actually show. Parts of the scene the cameras never see, such as behind closed doors, around solid walls, or up a staircase, are generated plausibly to keep the world explorable, so they won't match a real floor plan of those hidden areas.
For a full multi-room house or building:
* Capture open-plan areas where rooms share sightlines, and shoot from a few overlapping vantage points so most of the space stays in view. A [360 panorama](/marble/create/prompt-guides/pano-prompt) gives the most accurate single-space layout.
* To connect genuinely separate, walled-off rooms into one walkable building, generate each room on its own and arrange them with [Studio Compose](/marble/create/studio-tools/compose).
## Troubleshooting multi-image issues
### "Auto Layout not working properly"
* **Verify aspect ratios**: Ensure all images have exactly the same width-to-height ratio
* **Check for overlap**: Include visual elements that appear in multiple images
* **Improve image quality**: Use sharp, well-lit images with clear details
* **Check lighting consistency**: Try to match lighting conditions and color temperatures
* **Use images from same location**: Confirm all images are truly of the same space
Multi-image prompting allows you to build more complex, interesting worlds than single images alone.
The key is thoughtful planning of how your images relate spatially and visually to create cohesive, explorable environments.
# Pano prompt tips
Source: https://docs.worldlabs.ai/marble/create/prompt-guides/pano-prompt
Use panoramas for maximum control over the world.
# Creating worlds from panoramic images
Upload 360° panoramic images to create immersive 3D worlds with maximum control over your environment.
Panoramas provide complete spatial information, allowing for more accurate and detailed world generation than standard images.
## Getting started with panorama upload
You can obtain a full 360 equirectangular panorama by capturing it with a 360 camera,
rendering it from a 3D software, or downloading from a Marble world.
To create a world with panorama, you can simply drag and drop it to the omnibox; it
will expand with 2D input mode.
Check the left and right edge of your panorama is continuous for best results.
If the image is successfully recognized as a panorama, a
icon will appear on the image thumbnail. You can then start a
generation by clicking on Create.
## Troubleshooting common issues
### "Image is not recognized as a panorama"
* **Check aspect ratio**: Ensure exactly 2:1 width to height ratio
* **Check sky and ground coverage**: Phone panoramas often lack full vertical 180 coverage
### "Seam visible in generated world"
* **Fix source panorama**: Repair edge alignment in image editing software.
* **"Use advanced editing"**: to edit panorama with AI assistance on Marble.
## Frequently asked questions
### Are multi-panorama inputs supported?
* **Not directly**: Multiple panoramic images cannot be uploaded together in a single generation. However, you can create larger worlds by generating a world from each panorama separately, then stitching them together using [Studio compose](/marble/create/studio-tools/compose).
Panoramic uploads give you the most control over the final world layout, as the 360° image provides complete spatial information for the AI to work with.
# Video prompt tips
Source: https://docs.worldlabs.ai/marble/create/prompt-guides/video-prompt
How to create a world with a video
# Creating worlds from video
Upload short videos to create immersive 3D worlds with rich spatial information.
Videos provide multiple perspectives of your space, allowing the AI to understand depth
and spatial relationships better than a single images alone.
## Getting started with video upload
Drag a short video of a static space into the omnibox; it will expand with 2D input mode,
which allows you to start a generation by clicking on Create.
For video file specifications, see [Prompt guidelines →](/marble/create/prompt-guides)
## Best practices for video capture
Most artifacts in the generated world trace back to the source video, so the biggest improvements come from how you record.
### Camera movement guidelines
* **Rotation focus**: Rotate the camera to cover a large viewing angle
* **Avoid motion blur**: Use steady, controlled camera movements to avoid excessive motion blur
* **Wide coverage**: Aim to capture 180° to 360° of the space
* **Continuous shot**: Record one uninterrupted take of the space
### Camera settings
* **Fixed focal length**: Avoid zooming in or out during recording
* **Fixed exposure**: Avoid changing exposure during recording
## Troubleshooting common issues
### Artifacts or blur in the generated world
* **Re-record with steady motion**: Motion blur and shaky footage carry into the world as smeared or noisy geometry.
* **Keep the space static**: Avoid moving people or objects; the model reconstructs a static scene best.
* **Lock focal length and exposure**: Zooms and auto-exposure shifts during the take confuse depth estimation.
* **Export at full resolution**: Choose the 2M-splat SPZ or PLY rather than the low-res (500k) option for finer detail. See [Export file specs](/marble/export/specs).
* **Clean up specific spots**: Use [Pano edit](/marble/edit/pano-edit) for targeted fixes, or generate a [Variation](/marble/edit/variations) to get a cleaner result.
Video provides the richer spatial information for world generation, as the AI can analyze multiple perspectives and understand how different elements relate in 3D space.
# Compose
Source: https://docs.worldlabs.ai/marble/create/studio-tools/compose
Connect and arrange multiple worlds to create larger, seamless environments for games and experiences.
# Studio compose: building connected worlds
Use Studio Compose to connect multiple existing worlds into larger, seamless environments. Perfect for creating game maps, architectural complexes, or any scenario where you need to join separate scenes into one cohesive experience.
## Getting started with compose
The Studio Compose interface provides powerful tools for world arrangement:
* **3D Viewport**: The main canvas showing your connected worlds
* **Scene Panel**: Manage and add worlds to your composition
* **Controls Panel**: Fine-tune positioning, rotation, and scaling
* **Project Tools**: Save, share, and export your composed world
## How to add worlds to your composition
Build your composition by importing existing worlds:
1. **Click *Add scene***: In the Scene Panel, select **Add** to browse your worlds
2. **Choose your world**: Select from your saved worlds or community creations
3. **Position the world**: The world appears in the viewport with positioning handles
4. **Repeat as needed**: Add multiple worlds to build your larger environment
## How to build a multi-room house
Marble's image and Auto Layout inputs reconstruct a single space, so a whole house comes together by generating each room on its own and joining them here. There is no floor plan input that auto-connects rooms, so you place and connect them yourself.
1. **Generate each room**: Create each room as its own world (for example, a multi-image or panorama prompt per room).
2. **Add the rooms**: Use **Add scene** to bring each room world into the composition.
3. **Lay out to your plan**: Position, rotate, and scale each room to match your floor plan. Keep the plan open as a visual reference for placement, since Marble does not read it as a layout input.
4. **Connect the rooms**: Align floor heights and overlap shared walls or doorways so you can walk from room to room. See [How to fine-tune world connections](#how-to-fine-tune-world-connections).
## Splat removal
2. **Brush**: Use circle or square brushes to delete splats.
3. **Brush size**: Adjust the brush size for more precise control.
## How to position and align worlds
Precisely control world placement:
1. **Select a world**: Click on any world in the viewport or Scene Panel
2. **Use position controls**: In the Controls Panel, adjust X, Y, Z coordinates
3. **Set rotation**: Modify rotation values to orient worlds correctly
4. **Adjust scale**: Change the scale value to resize worlds proportionally
5. **Visual alignment**: Use the grid and bounding corners for visual reference
## How to navigate your composition
Move around your large-scale environment:
1. **Adjust movement speed**: Set movement speed (default: 3) for comfortable navigation
2. **Enable natural mouse**: Toggle natural mouse controls for intuitive camera movement
3. **Set field of view (FOV)**: Adjust to 92 or your preferred viewing angle
4. **Use grid reference**: Toggle grid visibility to help with alignment
5. **Show bounding corners**: Enable to see world boundaries clearly
## How to fine-tune world connections
Create seamless transitions between worlds:
1. **Check overlapping areas**: Look for where worlds meet or overlap
2. **Align ground levels**: Ensure floor heights match between connected worlds
3. **Match lighting**: Adjust worlds so lighting conditions blend naturally
4. **Test transitions**: Navigate between worlds to check for smooth movement
5. **Adjust background color**: Set consistent background (BG Color: #3f3f3f) across scenes
## How to save and export your composition
Preserve and share your connected world:
1. **Save your project**: Use the save controls to preserve your composition
2. **Share settings**: Enable sharing if you want others to view your creation
3. **Export options**: Use the export button to generate files for external use
4. **Monitor splats**: Keep track of your splat usage (500,000 / 2,000,000 limit shown)
## FAQ
### What does the splat count represent?
Splats represent the 3D Gaussian Splat data that makes up your worlds. The counter shows your current usage against your account limit. Larger, more detailed worlds use more splats.
### What is the Layer panel for?
The Layer panel lets you organize and manage the visibility of different worlds in your composition. You can hide or show specific worlds while working.
### How do I delete a world from my composition?
Select the world you want to remove and press delete key. The world will be removed from the composition but remains in your library.
# Record
Source: https://docs.worldlabs.ai/marble/create/studio-tools/record
Create cinematic camera animations and record smooth flythrough videos of your worlds.
# Record: creating camera animations
Use Record to create smooth camera animations and capture cinematic flythroughs
of your worlds. Perfect for showcasing environments, creating trailers, or producing
professional presentations of your 3D scenes.
**Important: data persistence limitation**
Currently the trajectory does not persist. You **WILL lose the keyframes** when you leave the page. Similarly, you **WILL lose the enhanced video** if you leave the page. Stay on the page until your (enhanced) videos have finished downloading.
## Getting started with record
The Studio Record interface provides comprehensive animation tools:
* **3D viewport**: View your world with camera animation preview
* **Camera frustum**: Yellow wireframe showing camera view and movement path
* **Animation timeline**: Control playback, timing, and keyframes
* **Playback controls**: Play, pause, and scrub through your animation
* **Export tools**: Enhance and export your final video
## How to set up your camera animation
Create smooth camera movements through your world:
1. **Position your camera**: Move to your desired starting viewpoint in the 3D viewport
2. **Set first keyframe**: The camera frustum (yellow wireframe) shows your view cone
3. **Move to next position**: Navigate to where you want the camera to move
4. **Add more keyframes**: Build your camera path with multiple positions
5. **Preview movement**: The yellow path lines show your camera's trajectory
## How to control animation timing
Fine-tune the pacing of your camera movement:
1. **Use the timeline**: The bottom timeline shows seconds
2. **Scrub through time**: Drag the blue playhead to preview different moments
3. **Adjust keyframe timing**: Move keyframes along the timeline to change pacing
4. **Set animation length**: Extend or shorten the total duration as needed
5. **Preview timing**: Use playback controls to test your animation speed
## How to preview your animation
Review your camera movement before recording:
1. **Click play**: Use the play button to start animation preview
2. **Pause and adjust**: Use pause to stop and make adjustments
3. **Scrub timeline**: Drag the playhead to jump to specific moments
4. **Check camera path**: Watch the yellow frustum move along the path
5. **View from animation**: The preview window shows your camera's perspective
## How to record and export
Capture your final animation:
1. **Preview first**: Ensure your animation looks correct using the preview controls
2. **Use enhance**: Click **Enhance** to improve video quality and effects. Wait on this page until the enhance is done.
3. **Download video**: Click **Download** to download your final animation file
## FAQ
### What does the yellow wireframe represent?
The **Camera frustum** (yellow wireframe cone) shows exactly what your camera sees at each moment, including the field of view and viewing direction. The lines connecting different positions show your camera's movement path.
### How long can my animation be?
The timeline extends beyond 8 seconds, allowing for longer animations. However, longer videos may take more time to process and export.
### What does the enhance feature do?
**Enhance** applies post-processing effects like improved lighting, color grading, and visual effects to make your video look more professional and cinematic.
### Can I edit keyframes after creating them?
Yes, you can move keyframes along the timeline to adjust timing, and reposition your camera at any keyframe to change the path.
### What file formats can I export?
A mp4 video.
### How do I create smooth camera movements?
Focus on gentle curves rather than sharp angles, use consistent speeds between keyframes, and preview frequently to ensure the movement feels natural.
## Keyboard shortcuts and controls reference
Master the Studio Record interface with these keyboard shortcuts and button tooltips for efficient animation workflow.
### Animation keyframe controls
Create and manage keyframes with these essential shortcuts:
* **F** - Add keyframe at current camera position
* **U** - Update selected keyframe to current camera position
* **Delete** / **Backspace** - Delete selected keyframe
### Timeline playback controls
Navigate through your animation timeline efficiently:
* **Space** - / Toggle play/pause animation
* **G** - Jump to beginning of timeline
* **H** - Jump to previous keyframe
* **L** - Jump to next keyframe
* **;** (semicolon) - Jump to end of timeline
### Seek and scrubbing controls
Fine-tune your position in the animation:
* **J** - Seek backward (hold to continue seeking)
* **K** - Seek forward (hold to continue seeking)
### Camera movement controls
Navigate your 3D world while setting up animations:
* **W** - Move forward
* **A** - Move left
* **S** - Move backward
* **D** - Move right
* **E** - Move up
* **Q** - Move down
* **Shift** - Speed up movement
### View controls
Adjust your viewing perspective:
* **\[** - Decrease field of view (FOV)
* **]** - Increase field of view (FOV)
* **0** - Return to origin position
### Timeline interface buttons
The timeline interface includes these interactive controls with tooltips:
#### Playback controls section
* "Jump to beginning (G)" - Moves playhead to start of timeline
* "Jump to previous keyframe (H)" - Moves playhead to previous keyframe
* / "Play" / "Pause (Space)" - Toggles animation playback
* "Jump to next keyframe (L)" - Moves playhead to next keyframe
* "Jump to end (;)" - Moves playhead to end of timeline
### Pro tips for efficient animation workflow
1. **Add keyframes frequently** - Add keyframes with F as you explore to build smooth camera paths
2. **Preview with Space** - Constantly test your animation timing with the play shortcut
3. **Scrub with H/L** - Fine-tune timing by holding these keys to seek through your animation
4. **Speed up navigation** - Hold Shift while moving to quickly position your camera
5. **Reset with 0** - Return to origin if you get lost while positioning your camera
# Edit worlds
Source: https://docs.worldlabs.ai/marble/edit/index
Modify and iterate on generated worlds using pano editing, expansion, and variations.
Marble provides several tools for modifying and enhancing your generated worlds. Each tool is designed for different types of edits:
Make targeted changes by editing the 360° panoramic view with natural language prompts.
Grow your worlds beyond their original boundaries by generating seamless extensions.
Generate alternative versions of your worlds while maintaining core elements and style.
# Pano edit
Source: https://docs.worldlabs.ai/marble/edit/pano-edit
Edit your world easily by editing a panorama.
# Pano edit: modifying worlds through panoramic views
Use Pano Edit to modify existing worlds by editing their 360° panoramic representation. This powerful tool lets you make targeted changes to your environments by describing edits in natural language and applying them to specific areas.
## Getting started with Pano edit
The Pano Edit interface provides intuitive tools for world modification:
* **360° panorama view**: The main viewport showing your world as a panoramic image
* **Edit prompt input**: Text field to describe your desired changes
* **Selection tools**: Add images and select edit areas for targeted modifications
* **Project panel**: Track your editing progress and manage versions
* **Apply controls**: Preview and apply your edits to create new world versions
## How to edit specific areas of your world
Make targeted changes to particular regions:
1. **Click *Select edit area***: Use the **Select edit area** button to define your target region
2. **Define the area**: Click and drag to outline the specific area you want to modify
3. **Describe your edit**: In the text prompt, describe how you want to change this area (e.g., "add snow-covered trees")
4. **Apply edit**: Click *Apply edit* to generate the modified version
5. **Review changes**: The system creates a new panorama with your modifications applied
## How to add reference images
Use additional images to guide your edits:
1. **Click *Add images***: Select **Add images** to upload reference material
2. **Choose your images**: Upload photos that show the style or elements you want to add
3. **Position references**: The images help guide the AI's understanding of your desired changes
4. **Combine with text**: Use both images and text prompts for more precise control
## How to navigate the panorama
Move around your 360° world view:
1. **Click and drag**: Use mouse controls to rotate and explore the panoramic view
2. **Use navigation**: The "360° Panorama View" indicator shows you can look in all directions
3. **Find your target**: Navigate to the area you want to edit before selecting it
4. **Center your view**: Position the area you want to change in the center for easier selection
## How to preview and apply changes
Review your edits before finalizing:
1. **Preview draft**: Use *Preview draft* to see your changes without committing
2. **Review the edit**: Check how your modifications look in the context of the full world
3. **Make adjustments**: If needed, refine your prompt or selection area
4. **Create world**: Click *Create world* to generate the final modified version
5. **Enable public mode**: Toggle on to share your edited world with the community
## How to manage your edit workflow
Track your editing progress effectively:
1. **Monitor stages**: The right panel shows your workflow from image input → pano (generated) → draft → world
2. **Save versions**: Each edit creates a new version while preserving the original
3. **Iterate safely**: Make multiple edits without losing previous versions
4. **Track progress**: See which stage of the editing process you're currently in
## Advanced editing techniques
### Descriptive edit prompts
* **Be specific**: "Replace the wooden fence with a stone wall covered in ivy"
* **Include style**: "Add Victorian-era street lamps with warm yellow lighting"
* **Mention materials**: "Change the pavement to cobblestones with moss between cracks"
* **Describe atmosphere**: "Make the sky stormy with dark clouds and lightning"
### Combining multiple edits
* **Sequential editing**: Apply one edit, then use the result for the next modification
* **Layered changes**: Build up complex modifications through multiple passes
* **Targeted regions**: Focus each edit on specific areas for precise control
## Common editing scenarios
### Weather and atmosphere changes
* Convert sunny scenes to rainy, snowy, or foggy conditions
* Adjust lighting from day to night or change seasons
* Add atmospheric effects like mist or storm clouds
### Architectural modifications
* Add or remove buildings, structures, or architectural elements
* Change building styles, materials, or colors
* Modify landscaping, paths, or outdoor furniture
### Environmental updates
* Change vegetation types or add/remove plants
* Modify terrain features like hills, water, or rocks
* Add or remove objects like vehicles, signs, or decorations
## FAQ
### How precise can my area selections be?
The **Select edit area** tool allows you to draw custom shapes around specific regions. You can be quite precise, but remember that the AI may affect surrounding areas slightly to ensure natural blending.
### Can I undo edits if I don't like the results?
Each edit creates a new version while preserving the original. You can always return to previous versions or start new edits from any saved stage in your workflow.
### What makes a good edit prompt?
Good prompts are specific, descriptive, and focused. Instead of "make it better," try "add colorful street art murals on the brick walls" or "replace the grass with a Japanese zen garden."
### Can I edit multiple areas at once?
Currently, you select and edit one area at a time. For multiple changes, apply edits sequentially, using each result as the base for the next modification.
### How do reference images help?
*Add images* provides visual examples of what you want to achieve. If you want to add specific architectural details, furniture, or artistic styles, reference images guide the AI more effectively than text alone.
### What's the difference between *Preview draft* and *Create world*?
*Preview draft* shows you the edit without finalizing it, letting you review and adjust. *Create world* commits the changes and creates the final, navigable 3D world.
### How does Public mode work in editing?
When enabled, *Public mode* makes your edited world visible in the community gallery. This is useful for sharing your creative modifications with other users.
### Can I edit worlds created by other users?
You can only edit worlds in your own library. However, if a world is shared publicly, you might be able to create your own version and then edit that copy.
# Variations
Source: https://docs.worldlabs.ai/marble/edit/variations
In Marble, you can **upload an image** to generate a 3D world. Once the world is created, you can modify the background by adjusting the **seed** for randomized variation, or by editing the **World Guide** for more precise control.
### 1. **Change the seed**
* Go to **Settings** in the bottom-left corner.
* Adjust the **Seed** value.
* Each new seed introduces **randomized background variations.**
* Example: changing the seed might transform a wall into a hallway or shift lighting.
👉 ***Try it: change the seed, hit Generate, and see what new background variation appears.***
### 2. **Edit the World Guide for more control**
If you want more **precise control** over what appears in the background, edit the **World Guide** directly.
* This lets you refine elements beyond random seed changes.
# Exporting to Blender
Source: https://docs.worldlabs.ai/marble/export/gaussian-splat/blender
Import Marble worlds into Blender using Gaussian Splatting plugins
There are a couple of Blender plugins available, and we've looked into the following.
### KIRI Engine
The [**KIRI Engine**](https://www.kiriengine.app/blender-addon/3dgs-render) plugin for Blender is a free plugin, and is the most well-known and actively maintained option! We've verified it works on Blender 4.2+.
### Reshot AI
Some users reported a better experience working with the [**Reshot AI**](https://github.com/ReshotAI/gaussian-splatting-blender-addon) plugin, preferring it over KIRI engine plugin due to its flexibility and slight performance increase.
This plugin also give you a little bit of lighting interaction where splats can produce lighting onto viewport meshes.
### SplatForge
The [**SplatForge**](https://superhivemarket.com/products/splatforge) plugin is not free, but it is one of the ones we utilize due to its performance / responsiveness at higher splat counts.
However, note that the SplatForge render pass is separate from Blender's main render loops (EEVEE/Cycles). This works fine for viewport overlays but additional compositor graphs are needed for offline renders.
Check out this example in our [blog post](https://www.worldlabs.ai/blog/marble-world-model) that was made with Marble worlds in SplatForge!
### Jetset iOS
The [Jetset iOS app](https://docs.lightcraft.pro/tutorials/blender-workflows/gaussian-splat-setup) allows you to first set up your splats in Blender via a modified Reshot AI plugin, then do virtual production on your phone! We've had a ton of fun with this one.
Check out this video for something made with Marble worlds in Jetset, and see our writeup [here](https://www.worldlabs.ai/case-studies/lightcraft)!
### Octane
The [**Octane**](https://help.otoy.com/hc/en-us/articles/6727419073435-OctaneRender-for-Blender-Installation-Guide) plugin is a paid option for Blender that allows for advanced lighting effects via the Octane pathtracer! Octane enables support for shadows, global illumination, and reflection/refraction with splats, producing more cohesive results when combining Marble worlds with other traditional assets.
Check out this video to see how to use Marble worlds with Octane!
# Community feedback and troubleshooting
If you have had positive or negative experiences with any of these plugins, we appreciate your feedback and will be updating this page as we go.
### Q: Why does the lighting look different in point cloud vs splat mode in ReshotAI? The lighting is darker in splat mode.
**A:** This is unavoidable due to how splats are represented as differently sized geometry in point cloud vs splat mode. In splat mode, the per-splat meshes are denser, so they occlude the lighting in the scene more. Neither lighting mode is "correct" - they're just different representations.
There are two solutions:
1. **Place lights within the scene:** Treat the splats as solid geometry and position your lights strictly within the scene.
2. **Make lights ignore splats for shadow-casting:** Click on the light → Object tab → Shading → Shadow Linking, then drag the splats into a new collection in that tab and uncheck it. This prevents the splats from casting shadows on other objects in your scene.
[discord discussion](https://discord.com/channels/1288765343552110637/1448516454055153674)
***
### Q: How does ReshotAI compare to other Blender plugins?
**A:** Based on user feedback, ReshotAI is the easiest and most straightforward option out of the plugins tested. While it may not look as great in point cloud mode (giving the Gaussian splats a more "dreamy" look), users have reported preferring it over other options like KIRI Engine and GS loader for its simplicity and ease of use. However, note that the GS loader method doesn't emit lights for objects in the scene, so it relies entirely on your scene lighting.
[discord discussion](https://discord.com/channels/1288765343552110637/1448516454055153674)
# Exporting to Houdini
Source: https://docs.worldlabs.ai/marble/export/gaussian-splat/houdini
Import Marble worlds into Houdini using Gaussian Splatting plugins
The [**GSOPs plugin**](https://github.com/cgnomads/GSOPs/tree/develop) is actively maintained and has a lot of great additional splat-related features around splat animation and splat conversions to vdb/mesh. We've verified it works on Houdini 20.5.
If you have had positive or negative experiences with this plugin, we appreciate your feedback and will be updating this page as we go.
# Exporting from Marble
Source: https://docs.worldlabs.ai/marble/export/gaussian-splat/index
Take your Marble content to popular 3D engines and platforms!
Exporting requires a paid plan. The [Standard plan](/marble/support/account-billing) covers splat exports (SPZ and PLY), 360 panoramas, and collider meshes, which is what you need for game engines like Unreal, Unity, Blender, and Houdini. High-quality textured mesh (GLB) export and commercial rights come with the [Pro plan](/marble/support/account-billing). The Free plan can generate worlds but not export them. See the [pricing page](https://marble.worldlabs.ai/pricing) for current plans.
## Format
We currently support **gaussian splats exports** for all our content.
You can find the file specs and sample files for different options in the
[Export file specs](/marble/export/specs#splats).
The lower-resolution files have been optimized to be as perceptually similar as possible to the higher-resolution files. For those of you working with applications where lighter compute is important, we encourage you to give this a try! You may convert these to .ply files [here](https://spz-to-ply.netlify.app) if needed.
## Integration
The [Radiance Fields](https://radiancefields.com/3d-gaussian-splatting-engine-support) website provides a comprehensive overview of platforms and plugins supporting splat integration. Here is a non-exhaustive subset of tools and platforms that we’ve either tested ourselves or received positive feedback from our user community about.
Click on one of these sub-categories to get started!
*Build custom applications using the spark.js framework for three.js developers.*
\
Provides the highest degree of control and customization for web-based applications. Perfect for creating VR experiences, interactive games, and custom visualization tools.
*Great for professional studios or creators using well-known tools like Unreal Engine, Unity, Houdini, or Blender.*
These integrate well with offline production pipelines in established 3D software ecosystems. They allow teams to fit Gaussian Splat workflows into existing VFX, animation, or game development pipelines without needing to build tooling from scratch.
**[Unreal Engine](/marble/export/gaussian-splat/unreal)**,
**[Unity](/marble/export/gaussian-splat/unity)**,
**[Blender](/marble/export/gaussian-splat/blender)**,
**[Houdini](/marble/export/gaussian-splat/houdini)**
*Great for artists or teams prioritizing one-stop solutions and fast iteration over deep customization.*
\
These focus on ease of sharing and distribution, often requiring minimal setup. Perfect for quickly showcasing work or creating interactive experiences that can be accessed directly in a browser.
We greatly appreciate the contributions and feedback from our user community on these so far— if you have insights or experiences with other export options, please share them with us on Discord and we will update these resources as we go!
# Exporting to dev frameworks
Source: https://docs.worldlabs.ai/marble/export/gaussian-splat/spark
Take Marble into your own custom apps!
For the three.js devs out there, check out [**spark**](https://sparkjs.dev/)! Some examples of what you can build on spark include [**lofi worlds**](https://lofiworlds.ai/marble) in VR, and a [**first person shooting game**](https://github.com/bmild/spark-physics) with animated characters.
We highly recommend this option. It is also what our own website is built off of, so you'll have the most native experience utilitizing this library. spark devs are also hanging out in our discord and are ready to answer your questions!
# Exporting to Unity
Source: https://docs.worldlabs.ai/marble/export/gaussian-splat/unity
Import Marble worlds into Unity using Gaussian Splatting plugins
The free [**aras-p plugin**](https://github.com/aras-p/UnityGaussianSplatting) works well on Unity 6.1! Users have also reported positive results with this plugin.
However, note that the main branch of this plugin has draw-order issues with multiple splats and problems with importing marble spzs.
We recommend downloading [this fork](https://github.com/winnie1994/UnityGaussianSplatting) in the meantime, which includes patches that resolves these issues.
## Community troubleshooting and feedback
If you have had positive or negative experiences with this plugin, we appreciate your feedback and will be updating this page as we go.
### Q: I'm getting an "Index out of range" error when trying to import 500k splats in Unity. The 2M splats work fine, but the low-res ones don't load.
**A:** This is a known issue with the 500k spz files. As a workaround, convert the 500k spz file to a ply file using the converter at [https://spz-to-ply.netlify.app/](https://spz-to-ply.netlify.app/). The converted 500k ply file should import into Unity without issues. [discord discussion](https://discord.com/channels/1288765343552110637/1421913319538954472)
***
### Q: How can I convert a splat into a working 3D mesh inside Unity so I can interact with it and try new things?
**A:** There's nothing inside Unity that supports converting splats to meshes. The only plugin we're aware of that supports this is the Houdini GSOPs plugin. We have collider meshes and high quality mesh baking natively in Marble though! [discord discussion](https://discord.com/channels/1288765343552110637/1421913319538954472)
***
### Q: My splat renders fine in Unity's Game view, but when I export to Quest 3 VR, I get a completely black screen. What's wrong?
**A:** Enable HDR on your URP asset. This simple setting fix resolves the black screen issue in VR builds. Make sure HDR is enabled in your Universal Render Pipeline asset settings. [discord discussion](https://discord.com/channels/1288765343552110637/1421913319538954472)
***
### Q: What Unity version should I use for VR projects with splats?
**A:** Use Unity 6.0 (specifically 6000.0.23f1 or similar). The aras-p plugin does not work for VR on Unity 6.3. After downgrading to 6.0, make sure you have all the XR packages installed and check your graphics API settings. [discord discussion](https://discord.com/channels/1288765343552110637/1421913319538954472)
***
### Q: What are the recommended settings for getting splats working in VR on Quest 3?
**A:** Here's the recommended setup:
* **Unity Version:** 6.0 (6000.0.23f1 or similar)
* **Plugin:** aras-p UnityGaussianSplatting package
* **Render Pipeline:** URP (Universal Render Pipeline) with HDR enabled
* **Graphics API:** Vulkan
* **Rendering Mode:** Multi-view (not Single Pass Instanced - SPI causes black screens when the headset is active)
* **XR packages:** Make sure all XR packages are installed
Both URP and BiRP (Built-in Render Pipeline) work, but URP is recommended. The visual quality looks the same between them. [discord discussion](https://discord.com/channels/1288765343552110637/1421913319538954472)
***
### Q: I'm getting a "PLY vertex size mismatch, expected 252 but file has 68" error with the ninjamode Gaussian splat VR plugin. The same file works fine with the aras plugin.
**A:** Unfortunately, we haven't tested the ninjamode plugin ourselves and can't advise on it. The aras-p plugin is the recommended solution for Unity splat imports. We've put the ninjamode plugin on our list to try out in the future. [discord discussion](https://discord.com/channels/1288765343552110637/1421913319538954472)
***
### Q: I can't get Multi-pass rendering to work in VR - it automatically switches back to Single-pass when I open it in VR.
**A:** This is expected behavior. Multi-view rendering works, but Single Pass Instanced (SPI) causes black screens when the headset is active. Stick with Multi-view rendering for VR builds. [discord discussion](https://discord.com/channels/1288765343552110637/1421913319538954472)
***
### Q: What's the performance like with splats on Quest 3?
**A:** Based on testing:
* **2M splat files:** Cause Quest 3 builds to crash when opening. Not recommended for standalone VR.
* **500k splat files:** Work better for VR, with better small details than 2M files in some cases. Performance is around 12fps in Unity, compared to 19fps in PlayCanvas (via Oculus browser). The 500k files are more suitable for standalone VR, while 2M files may only be viable for desktop VR. [discord discussion](https://discord.com/channels/1288765343552110637/1421913319538954472)
***
### Q: I need to place assets inside my Marble environment, but they're falling through the floor even though I have a mesh collider component on the GLB. Any tips?
**A:** This question was raised but not fully resolved in the discussion. The GLB mesh collider may need additional configuration. We're planning to support coarse nav meshes and collider meshes natively in Marble soon, which should help with physics interactions. [discord discussion](https://discord.com/channels/1288765343552110637/1421913319538954472)
***
### Q: Which splat export format should I use for Unity?
**A:** Marble offers three export options:
* **2M ply** - Full resolution PLY format
* **2M spz** - Full resolution SPZ format
* **500K spz** - Lower resolution SPZ format
For Unity, the 2M spz files work out of the box. The 500k spz files have a known issue and need to be converted to ply format using [https://spz-to-ply.netlify.app/](https://spz-to-ply.netlify.app/) before importing. [discord discussion](https://discord.com/channels/1288765343552110637/1421913319538954472)
***
### Q: The aras plugin works in the sample project, but when I try to use it in an empty project, the .ply file doesn't show up at all.
**A:** Make sure you have all the required XR packages installed and that your graphics API is set correctly (Vulkan for VR). Also ensure HDR is enabled on your URP asset. Try recreating the setup from the aras-p sample project to ensure all dependencies are properly configured. [discord discussion](https://discord.com/channels/1288765343552110637/1421913319538954472)
# Exporting to Unreal Engine
Source: https://docs.worldlabs.ai/marble/export/gaussian-splat/unreal
Import Marble worlds into Unreal Engine using Gaussian Splatting plugins
Several Unreal Engine plugins are available for Windows.
### VIVE Mars Nova 3DGS
The [**VIVE Mars Nova 3DGS**](https://vive-mars.notion.site/VIVE-Mars-Nova-3DGS-2a87f6cef4d880ddb37af4fcb8430779) plugin imports `.ply` and `.spz` splats into Unreal Engine with a drag-and-drop workflow and renders them through Unreal Engine's Niagara system. The current private beta is available by invitation and supports Unreal Engine 5.4–5.6.
This walkthrough shows how to import a Marble splat and configure collision and environment lighting in Unreal Engine.
### Volinga
The [**Volinga**](https://web.volinga.ai/#VolingaPlugin) plugin imports and renders `.ply` and `.nvol` splats in Unreal Engine. It supports additive lighting and multiple splats in one level; Volinga Plugin Pro adds proxy-mesh support and HDR workflows. Volinga is paid, and its watermarked 14-day trial is limited to non-commercial use.
This walkthrough shows how to relight a Marble splat in Unreal Engine with Volinga. Volinga also documents a production that used Marble worlds on an LED volume [here](https://web.volinga.ai/bringing-ai-generated-worlds-to-the-led-volume/).
### Akiya
The [**3D Gaussians Plugin**](https://vrlab.akiya-souken.co.jp/en/products/threedgaussianplugin/) from Akiya Research Institute imports and renders `.ply` splats in Unreal Engine. Its render modes include lit, masked, and shadow-casting options. The plugin is available through [Fab](https://www.fab.com/listings/43088b82-e55a-4ea8-a453-f0774f10d5a2) as a one-time purchase.
This walkthrough shows how to export a Marble world to Unreal Engine with the Akiya plugin. Gaussian Mansion also used this plugin; see our [case study](https://www.worldlabs.ai/case-studies/3-gaussian-mansion) and the [game website](https://tijerinart.itch.io/gaussian-mansion).
### Postshot
The [**Postshot**](https://www.jawset.com/docs/d/Postshot+User+Guide/Unreal+Engine+Integration) plugin for Unreal works reliably (we've verified it works on UE5.2)! A free version is available, but you need to upgrade for commercial / production use.
It requires standalone Postshot software to be installed: you'll import .ply files into Postshot and save out in postshot format (.psht) before loading into Unreal.
Check out this walkthrough video for Postshot from one of our users!
### Luma / XVerse
While the [XVerse](https://github.com/xverse-engine/XScene-UEPlugin) and [Luma](https://www.fab.com/listings/b52460e0-3ace-465e-a378-495a5531e318) plugins are completely free, they are not actively maintained and our users have had limited success with these.
In particular, XVerse is functional on UE5.2 but comes with visual artefacts from under-the-hood aggressive optimizing / downsampling.
## Community troubleshooting and feedback
If you have had positive or negative experiences with any of these plugins, we appreciate your feedback and will be updating this page as we go.
### Q: Which Unreal Engine plugin supports depth of field with splats and meshes together?
**A:** For depth of field support, **Volinga** is potentially the best option, especially for virtual production use cases. The **3D Gaussians Plugin** (Akiya) also supports depth of field and works with nDisplay. Note that Postshot didn't work well with nDisplay according to user reports. [discord discussion](https://discord.com/channels/1288765343552110637/1422034485934821497)
***
### Q: How can I get depth of field to work with splats in Unreal Engine?
**A:** You can change the splat material from translucent to masked with AA Temporal Dither. This allows depth of field to work correctly, but note that it will slightly lower the visual quality compared to translucent materials. To do this, open the large Niagara node and look at the bottom section where you'll see the material your particles use. Change the material blend mode from translucent to masked and enable AA Temporal Dither. [discord discussion](https://discord.com/channels/1288765343552110637/1422034485934821497)
***
### Q: Why do my splats look low resolution in Unreal compared to viewing them on the Marble site?
**A:** The **XVerse** plugin does internal downsampling that causes artifacts and reduces splat density. This is a known issue with that plugin. **Postshot** doesn't have this low resolution issue. Also, make sure you're not downloading files as "SPZ Low-res" format, as that will naturally have fewer splats. [discord discussion](https://discord.com/channels/1288765343552110637/1422034485934821497)
***
### Q: Does Postshot add a watermark to my Unreal Engine scenes?
**A:** Yes, Postshot implements a watermark icon in the Unreal Engine scene when you import a .psht file. This watermark appears in the scene view. [discord discussion](https://discord.com/channels/1288765343552110637/1422034485934821497)
***
### Q: Can I still use the Luma plugin for Unreal Engine?
**A:** No, Luma no longer supports its Unreal Engine plugin and it won't work properly. The plugin was discontinued and is not actively maintained. Even in UE 5.3 (the last supported version), the plugin doesn't function correctly. [discord discussion](https://discord.com/channels/1288765343552110637/1422034485934821497)
***
### Q: How can I convert SPZ files to PLY format for use with different plugins?
**A:** You can use the online converter at [https://spz-to-ply.netlify.app/](https://spz-to-ply.netlify.app/) to convert SPZ files to PLY format. This is useful if you need to use a plugin that requires PLY format or if you're experiencing issues with SPZ files. [discord discussion](https://discord.com/channels/1288765343552110637/1422034485934821497)
***
### Q: Which plugin works best for virtual production with nDisplay?
**A:** **Volinga** is the recommended option for virtual production and nDisplay compatibility. Postshot had issues with nDisplay according to user reports. The **3D Gaussians Plugin** (Akiya) also supports depth of field and works with nDisplay. [discord discussion](https://discord.com/channels/1288765343552110637/1422034485934821497)
***
### Q: I'm getting texture errors when importing PLY files with XV3DGS in Unreal Engine 5.5. Is this normal?
**A:** Yes, this is a known issue. In UE 5.5, XV3DGS shows texture errors when importing PLY files. On first import, you may see an empty cube, and after reloading, you may see a gray mesh. This is expected behavior with this plugin in 5.5. [discord discussion](https://discord.com/channels/1288765343552110637/1422034485934821497)
***
### Q: My material status shows "none" after importing with XV3DGS. Will anything work?
**A:** If the material status is "none", the splats won't render properly. Try exporting your Marble world as SPZ format and converting it to PLY using [https://spz-to-ply.netlify.app/](https://spz-to-ply.netlify.app/) before importing. Make sure you haven't modified the particle material or textures in the import folder. [discord discussion](https://discord.com/channels/1288765343552110637/1422034485934821497)
***
### Q: What's the best plugin for depth of field and nDisplay support if I'm willing to pay?
**A:** According to the linked user reports, the **3D Gaussians Plugin** (Akiya) is the most reliable option for this use case. It supports depth of field, works with nDisplay, has an automatic splitting system to divide scenes into multiple Niagara effects for full quality, and provides better quality than XVerse. [discord discussion](https://discord.com/channels/1288765343552110637/1422034485934821497)
***
### Q: Can I change the material settings in XVerse/XScene-UEPlugin to enable depth of field?
**A:** While it's theoretically possible to change the material from translucent to masked in the Niagara component, users have reported that Unreal Engine crashes when attempting this with the XScene-UEPlugin. The material settings may be locked or cause instability with this particular plugin. [discord discussion](https://discord.com/channels/1288765343552110637/1422034485934821497)
***
### Q: Does Postshot still allow exporting to PLY format in the free version?
**A:** No, the Postshot plugin no longer allows exporting as PLY if you don't have a paid subscription. This is a limitation of the free version. [discord discussion](https://discord.com/channels/1288765343552110637/1422034485934821497)
# Mesh export
Source: https://docs.worldlabs.ai/marble/export/mesh
# Mesh export: downloading 3D assets
Export your Marble worlds as 3D meshes for use in game engines, 3D software, and other
applications. Choose from multiple mesh formats and quality levels to suit your specific
needs, from quick prototyping to high-quality production assets.
Collider mesh export is included with the [Standard plan](/marble/support/account-billing). High-quality textured mesh (GLB) export requires the [Pro plan](/marble/support/account-billing).
## Getting started with collider mesh export
You can export collider mesh along with a splat to provide simple physics for games.
For example, see [**first person shooting game**](https://github.com/bmild/spark-physics).
To download collider mesh, navigate to the download menu from
world viewer, or world in Worlds, and download from the
Collider Mesh (GLB) link.
## Getting started with high-quality mesh export
1. **Trigger offline generation**: Select
**High-quality mesh (GLB)** from your world's
export options
2. **Wait for processing**: High-quality mesh generation takes up to an hour to complete
3. **Continue working**: You can close tabs and browser windows - the process continues in the background
4. **Download when ready**: Return later to find a
**High-quality mesh (GLB)** button replacing the generate option
5. **Access premium quality**: Download detailed meshes, one around 600k triangles and
texture maps, another around 1M triangles with vertex colors.
## FAQ
### How long does offline mesh generation take?
High-quality mesh generation could take up to 1 hour, depending on world complexity and
system load. You can close your browser and the process will continue in the background.
### Can I use collider meshes for visual rendering?
No. **Collider meshes** are optimized for physics interactions and have simplified geometry.
For visual rendering, use splats or the high-quality offline-generated meshes instead.
### Why does my high-quality mesh have artifacts or bad geometry?
Meshes are derived from the world, while the Gaussian splat is Marble's highest-fidelity
representation, so some reconstruction artifacts are expected. Uneven geometry, holes, and
blobby or "floater" surfaces show up most in regions that are hard to reconstruct: thin or
complex structures, transparent or reflective surfaces, sky and background, and areas the
input views didn't cover well.
To get a better result:
* For the cleanest visual quality, use a [splat export](/marble/export/gaussian-splat) instead of a mesh.
* Compare both high-quality mesh files. The \~600k-triangle textured GLB and the \~1M-triangle vertex-colored GLB can differ in where artifacts appear, so one may suit your scene better.
### How do I know when my offline mesh is ready?
Return to your world's download menu after several hours.
The **High-quality mesh (GLB)** or option will be
replaced with a **High-quality mesh (GLB)** button when processing
is complete.
### What's included with high-quality meshes?
High-quality meshes include detailed geometry (around 600k triangles), and texture maps
. Some versions also include vertex color data for additional
material flexibility. See examples on [Export file specs →](/marble/export/specs).
### Can I cancel offline mesh generation?
Currently, once started, offline mesh generation runs to completion in the background.
### What's the file size of exported meshes?
File sizes vary by complexity and format. Collider mesh are typically 3-4 MB,
while high-quality meshes with textures are typical around 100 - 200 MB depending on
world details. See examples on [Export file specs →](/marble/export/specs).
# Export file specs
Source: https://docs.worldlabs.ai/marble/export/specs
Tech specs on export files
### Images
* **Prompt image**:
* prompt from which the world is generated
* **360 panorama**:
* Equirectangular png of 2560 x 1280 pixels
### Splats
* **Splats (SPZ)**:
* Splat-based format optimized for Marble's rendering system, about 2M splats
* **Splats (low-res SPZ)**:
* Splat-based format optimized for Marble's rendering system, about 500k splats
* **Splats (PLY)**:
* Splat file with broader software compatibility, about 2M splats
* **Splats (low-res PLY)**:
* Splat file with broader software compatibility, about 500k splats
### Mesh
* **Collider Mesh (GLB)**
* coarse mesh optimized for simple physics calculations
* glb format
* 100-200k triangles
* **High-quality mesh (GLB)**
* One glb around 600k triangles, with texture information
* Another glb around 1M triangles, with vertex colors
* Takes up to an hour to generate
* Currently rate limited to 4 generation requests per hour per user
* You can only generate high quality mesh on worlds you own
## Example files
Here we provide a few example scenes and export files to test against.
### Gaussian splats
Scroll to the right to see all options.
| Scene | SPZ 2m | SPZ 500k | PLY 2m | PLY 500k |
| ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Rustic kitchen with natural light](https://marble.worldlabs.ai/world/69a9fc22-63ad-4e4c-9514-065b9aa56340) | [](https://wlt-ai-cdn.art/example_exports/rustic_kitchen_with_natural_light/rustic_kitchen_with_natural_light_2m.spz) | [](https://wlt-ai-cdn.art/example_exports/rustic_kitchen_with_natural_light/rustic_kitchen_with_natural_light_500k.spz) | [](https://wlt-ai-cdn.art/example_exports/rustic_kitchen_with_natural_light/rustic_kitchen_with_natural_light_2m.ply) | [](https://wlt-ai-cdn.art/example_exports/rustic_kitchen_with_natural_light/rustic_kitchen_with_natural_light_500k.ply) |
| [Elegant library with fireplace](https://marble.worldlabs.ai/world/20fc27f9-5b1f-4c76-8b22-67b866195aaf) | [](https://wlt-ai-cdn.art/example_exports/elegant_library_with_fireplace/elegant_library_with_fireplace_2m.spz) | [](https://wlt-ai-cdn.art/example_exports/elegant_library_with_fireplace/elegant_library_with_fireplace_500k.spz) | [](https://wlt-ai-cdn.art/example_exports/elegant_library_with_fireplace/elegant_library_with_fireplace_2m.ply) | [](https://wlt-ai-cdn.art/example_exports/elegant_library_with_fireplace/elegant_library_with_fireplace_500k.ply) |
| [Modern house with lush landscaping](https://marble.worldlabs.ai/world/e1d2610d-32a7-4364-acbb-8fcc97c1933d) | [](https://wlt-ai-cdn.art/example_exports/modern_house_with_lush_landscaping/modern_house_with_lush_landscaping_2m.spz) | [](https://wlt-ai-cdn.art/example_exports/modern_house_with_lush_landscaping/modern_house_with_lush_landscaping_500k.spz) | [](https://wlt-ai-cdn.art/example_exports/modern_house_with_lush_landscaping/modern_house_with_lush_landscaping_2m.ply) | [](https://wlt-ai-cdn.art/example_exports/modern_house_with_lush_landscaping/modern_house_with_lush_landscaping_500k.ply) |
| [Narrow European cobblestone lane](https://marble.worldlabs.ai/world/54fad6e4-9c9b-43ba-be6d-f1e31cbe7a95) | [](https://wlt-ai-cdn.art/example_exports/narrow_european_cobblestone_lane/narrow_european_cobblestone_lane_2m.spz) | [](https://wlt-ai-cdn.art/example_exports/narrow_european_cobblestone_lane/narrow_european_cobblestone_lane_500k.spz) | [](https://wlt-ai-cdn.art/example_exports/narrow_european_cobblestone_lane/narrow_european_cobblestone_lane_2m.ply) | [](https://wlt-ai-cdn.art/example_exports/narrow_european_cobblestone_lane/narrow_european_cobblestone_lane_500k.ply) |
| [Warm traditional kitchen interior](https://marble.worldlabs.ai/world/30ac948d-6b19-4191-a12e-4ce4510ccfe7) | [](https://wlt-ai-cdn.art/example_exports/warm_traditional_kitchen_interior/warm_traditional_kitchen_interior_2m.spz) | [](https://wlt-ai-cdn.art/example_exports/warm_traditional_kitchen_interior/warm_traditional_kitchen_interior_500k.spz) | [](https://wlt-ai-cdn.art/example_exports/warm_traditional_kitchen_interior/warm_traditional_kitchen_interior_2m.ply) | [](https://wlt-ai-cdn.art/example_exports/warm_traditional_kitchen_interior/warm_traditional_kitchen_interior_500k.ply) |
### Image & mesh
| Scene | 360 Pano | Collider mesh GLB | HQ mesh GLB |
| ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| [Rustic kitchen with natural light](https://marble.worldlabs.ai/world/69a9fc22-63ad-4e4c-9514-065b9aa56340) | [](https://wlt-ai-cdn.art/example_exports/rustic_kitchen_with_natural_light/rustic_kitchen_with_natural_light_pano.png) | [](https://wlt-ai-cdn.art/example_exports/rustic_kitchen_with_natural_light/rustic_kitchen_with_natural_light_collider.glb) | [](https://wlt-ai-cdn.art/example_exports/rustic_kitchen_with_natural_light/rustic_kitchen_with_natural_light_hq.glb) |
| [Elegant library with fireplace](https://marble.worldlabs.ai/world/20fc27f9-5b1f-4c76-8b22-67b866195aaf) | [](https://wlt-ai-cdn.art/example_exports/elegant_library_with_fireplace/elegant_library_with_fireplace_pano.png) | [](https://wlt-ai-cdn.art/example_exports/elegant_library_with_fireplace/elegant_library_with_fireplace_collider.glb) | [](https://wlt-ai-cdn.art/example_exports/elegant_library_with_fireplace/elegant_library_with_fireplace_hq.glb) |
| [Modern house with lush landscaping](https://marble.worldlabs.ai/world/e1d2610d-32a7-4364-acbb-8fcc97c1933d) | [](https://wlt-ai-cdn.art/example_exports/modern_house_with_lush_landscaping/modern_house_with_lush_landscaping_pano.png) | [](https://wlt-ai-cdn.art/example_exports/modern_house_with_lush_landscaping/modern_house_with_lush_landscaping_collider.glb) | [](https://wlt-ai-cdn.art/example_exports/modern_house_with_lush_landscaping/modern_house_with_lush_landscaping_hq.glb) |
| [Narrow European cobblestone lane](https://marble.worldlabs.ai/world/54fad6e4-9c9b-43ba-be6d-f1e31cbe7a95) | [](https://wlt-ai-cdn.art/example_exports/narrow_european_cobblestone_lane/narrow_european_cobblestone_lane_pano.png) | [](https://wlt-ai-cdn.art/example_exports/narrow_european_cobblestone_lane/narrow_european_cobblestone_lane_collider.glb) | [](https://wlt-ai-cdn.art/example_exports/narrow_european_cobblestone_lane/narrow_european_cobblestone_lane_hq.glb) |
| [Warm traditional kitchen interior](https://marble.worldlabs.ai/world/30ac948d-6b19-4191-a12e-4ce4510ccfe7) | [](https://wlt-ai-cdn.art/example_exports/warm_traditional_kitchen_interior/warm_traditional_kitchen_interior_pano.png) | [](https://wlt-ai-cdn.art/example_exports/warm_traditional_kitchen_interior/warm_traditional_kitchen_interior_collider.glb) | [](https://wlt-ai-cdn.art/example_exports/warm_traditional_kitchen_interior/warm_traditional_kitchen_interior_hq.glb) |
## FAQ
### What's the difference between SPZ and PLY formats?
SPZ is Marble's native splat format optimized for file size, while PLY is a
uncompressed format compatible with more Gaussian splat software packages.
### Why are my splats / meshes upside-down when I export them to other software?
Default world labs worlds are in OpenCV coordinate system (+x left, +y down, +z forward).
Many DCC software are in the OpenGL coordinate system (+x left, -y down, -z forward).
To correct for it, perform an OpenCV-to-OpenGL transformation by scaling the Y and Z
axes by -1 (keeping X unchanged).
[See more on coordinate systems.](https://stackoverflow.com/questions/44375149/opencv-to-opengl-coordinate-system-transform)
# Models
Source: https://docs.worldlabs.ai/marble/models
An overview of available Marble models
## Choosing a model
If you're just starting out with Marble, we recommend using **Marble 1.1**.
For the largest worlds, **Marble 1.1 Plus** automatically expands its coverage of 3D space to generate bigger worlds when possible.
Marble also offers other models optimized for different workflows, balancing speed, cost, and quality.
Our latest and most advanced model for creating the largest worlds.
This model automatically expands its coverage of 3D space to generate bigger worlds when possible.
Price per generation: 1,500 base cost + 0-1,500 variable world generation cost
World generation with improved quality at a fixed cost
Price per generation: 1,500 credits
Legacy model retained for ongoing explorations
Price per generation: 1,500 credits
Our fastest model, ideal for quickly exploring ideas and testing prompts.
Price per generation: 150 credits
# Release notes
Source: https://docs.worldlabs.ai/marble/release-notes
Latest updates and improvements to Marble
## April 2, 2026
### Features and improvements
* **Added new Marble 1.1 and Marble 1.1 Plus models** -- see [Models](/marble/models) for more details
* Existing standard and draft models are still available as Marble 1.0 and Marble 1.0 Draft, respectively
* Added new model selector to the omnibox and other Create surfaces
* Model used to generate a world or draft is now visible in your assets page
* Advanced editing is now exposed as *Create & edit* as an option in the omnibox
* Visual, layout, and performance improvements to the assets page
### Bug fixes
* Fixed an issue where multiple projects open across multiple tabs would conflict
* Fixed an issue with visibility not cascading into child nodes in Studio
## January 29, 2026
### Features and improvements
* Meshes can now be imported into Composer
* **Cancel subscription** button is easier to find
* Video prompt file **size** limit for the API **increased** to 100MB
## January 1, 2026
### Features and improvements
* Improved export options
* Assets are now roughly scaled and grounded to better match real-world units
* Option to choose between \*\*OpenGL \*\*and **OpenCV** coordinate systems
* Customize export preferences for **high-quality meshes** (vertex-colored vs textured)
## December 18, 2025
### Features and improvements
* You can now copy & paste in Chisel using Ctrl+C / Ctrl+V
* Expand mode now provides clearer guidance on valid expansion regions, including more **explicit feedback** when the target position is too high or too low in the scene
## December 11, 2025
### Features and improvements
* Record mode in Studio now gives you more creative control: You can now freely move the floating preview window as you plan your flythrough, and you now have **expanded video export settings for quality**, resolution, aspect ratio, frame rate and codec (compression format) to tailor your final video.
* Improved export compatibility with external tools: World generations now export in the OpenGL coordinate system (previously OpenCV) for both splats and meshes. SPZ splats in Studio now export by default in SPZ v2 format (v3 still available as opt-in).
### Bug fixes
* Fixed bugs around world generation status and added better error messaging for failed generations.
* Fixed bug around display of thumbnails for in-progress world generations.
* Fixed a bug in Compose mode within Studio where importing the same world twice caused edits to be shared across both imported worlds.
* Fixed bugs around payment downgrade/cancellation processing.
## December 5, 2025
### Features and improvements
* Added ability to take screenshots of 360° panoramas.
* Added option to remove previously linked payment methods from your account.
* Upgraded pano editing to the latest high-quality model version. As part of this update, the credit cost per pano edit has been adjusted from **50 to 150 credits**.
### Bug fixes
* Fixed issue that prevented some projects from loading in Marble Studio.
* Fixed transform handles not being clickable in **Compose** mode.
* Fixed bug causing **video export in Animate** mode to fail on certain devices.
* Fixed issue where **Chisel panorama view** restricted camera controls after returning to the page.
* Fixed several bugs around **payment success**, **downgrades**, and **cancellation** flows.
## November 20, 2025
### Features and improvements
* Marble Studio now supports editing and composing significantly larger worlds using a new **Level-of-Detail splat-rendering backend**. Performance should remain stable as you scale up to more worlds and higher splat counts.
* **World IDs** are now displayed in the Worlds section, making it easier to share them with support for debugging.
### Bug fixes
* Fixed bug where some world generations appeared to be ongoing/spinning for long durations of time without finishing.
* Fixed bug where users could exceed their available credit balances and were subsequently charged for the overage amount when upgrading their subscription plans. Also corrected a credit-to-dollar conversion issue that could overstate the overage amount. Refunds have been issued and all balances were restored to their original subscription credit amounts.
* Fixed bug that caused some users to be unable to upgrade their subscriptions.
# Subscriptions & billing
Source: https://docs.worldlabs.ai/marble/support/account-billing
Manage your Marble subscription, credits, and billing settings.
# Subscriptions & billing
## How does Marble's pricing work?
Marble uses a credit-based subscription system, with higher tiers unlocking more features and credits. This guide explains how the plans, credits, and feature unlocks work so you can choose the right plan for your needs.
Credits are used each time you take an action in Marble.
Our current pricing can be found [here](https://marble.worldlabs.ai/pricing).
## What are the different subscription options?
Marble offers four subscription tiers, each with a monthly credit allocation and access to different capabilities.
### Free plan
Good for exploring the basics of what Marble can do
* Lightweight intro to Marble that lets you generate worlds from a text prompt, single image, or 360 panorama
* Includes up to 4 world generations
### Standard plan
Best for hobbyist users creating and editing worlds
* Adds creation tools for richer world building, including:
* Generation from multiple images, videos, or 3D layouts
* Editing your worlds
* Exporting your worlds
* Downloading worlds from the Marble community
* Includes up to 12 world generations
### Pro plan
Ideal for professional creators, artists, designers and engineers
* Unlocks advanced workflows, including:
* Expanding worlds to larger spaces
* Enhancing the quality of generated video outputs
* Exporting high-quality textured meshes
* Includes commercial rights to generated worlds
* Includes up to 25 world generations
### Max plan
Designed for users creating at scale
* Enables high-volume production
* Includes up to 75 world generations
## Do you offer an enterprise plan?
Yes, we offer custom plans for large teams and organizations that need flexible solutions at scale. [Contact us here](https://marble.worldlabs.ai/enterprise) to talk to our sales team.
## How do credits work?
Credits are used each time you take an action in Marble. The number of credits used depends on the complexity of the action (see current pricing [here](https://marble.worldlabs.ai/pricing)).
For example, a basic world generation from a single image uses a combination of 1,500 "world generation" credits + 80 "input method" credits, for a total of 1,580 credits.
An advanced world generation from multiple images that is then edited twice and expanded once uses a combination of 100 "input" credits + 1,500 "world generation + (2 × 150) "edit pano" credits + 2,000 "expand world" credits, for a total of 3,900 credits.
## Can I use my Marble credits with the World API?
No. Marble and the World API bill separately, and credits are not interchangeable in either direction.
* Marble credits come with your subscription at [marble.worldlabs.ai](https://marble.worldlabs.ai) and are spent on actions in the Marble app.
* API credits are purchased separately on the [World Labs Platform](https://platform.worldlabs.ai/billing) and are spent on API requests.
Subscribing to a Marble plan does not give you API credits, and API credits cannot be spent in Marble. Credits cannot be transferred or converted between the two.
This holds even though you sign in to both with the same email — they are separate accounts with separate balances and separate billing. Your Marble plan therefore tells you nothing about your API charges: you can be on the Marble Free plan and still receive an invoice for API usage. See [API pricing](/api/pricing) for how API billing works.
If you bought a Marble subscription intending to use the API, contact [support@worldlabs.ai](mailto:support@worldlabs.ai).
## Do unused credits roll over?
Unused credits that come with your subscription plan (for example, the 20,000 credits that come with a Standard plan) do not roll over to the next month of your account billing cycle.
Top-up credits work differently: they roll over to the next month and expire 1 year from the date of purchase. When you take an action in Marble, your subscription credits are used first, before any top-up credits you've purchased.
## How do I purchase additional credits?
All paid plans allow you to purchase additional top-up credits at any time when you run out.
To buy top-up credits, navigate to your Account page on the lower left corner of Marble, click "Manage account," and on the Billing page click "Purchase credits." Select a credit package, then confirm the purchase with the payment method on your account.
Purchasing top-up credits requires a paid plan. On the Free plan the "Purchase credits" button is disabled, so subscribe to a paid plan first.
If you need a billing change that you can't make from your account, email [support@worldlabs.ai](mailto:support@worldlabs.ai). Credits can't be added through the Discord community channels, so don't post your account email there.
## Will my plan automatically renew?
Yes, your plan will automatically renew at the end of your billing cycle.
## How do I upgrade my plan?
You can upgrade your plan by navigating to your Account page on the lower left of Marble, clicking "Manage account," clicking "Manage subscription," and selecting the plan you'd like to upgrade to.
Your upgrade will be effective immediately and you'll have access to the new tier's features right away.
You'll receive a prorated refund for any unused credits from your old plan, calculated using the per credit cost of your old plan. This refund will automatically be applied to the total cost of your new plan, and you will be invoiced for the remaining balance.
For example, if you upgrade from Standard to Pro tier and currently have 5,000 credits remaining in your Standard plan, you'll receive a refund of 5,000 × (\$20/month for Standard plan ÷ 20,000 credits in Standard plan) = \$5. Your upgrade cost will then become \$35/month for Pro plan, less the \$5 credit, for a net cost of \$30.
Any top-up credits you have in your account won't be affected and will roll over into your new plan.
## How do I downgrade my plan?
You can downgrade your plan by navigating to your Account page on the lower left corner of Marble, clicking "Manage account," clicking "Manage subscription," and selecting the plan you'd like to downgrade to. You'll retain access to all the features of your old plan until the end of your billing period, at which point you'll be downgraded to the new plan. Note that no pro-rated refund will be provided.
Any top-up credits you have in your account won't be affected and will roll over into your new plan.
## How do I cancel my plan?
You can cancel your plan by navigating to your Account page on the lower left corner of Marble, clicking "Manage account," clicking "Manage subscription," and clicking the "Cancel subscription" button beneath your current plan. You'll retain access to all the features of your current plan until the end of your billing period, at which point you'll return to the Free plan. Note that no pro-rated refund will be provided.
## How do I delete my account?
If you wish to close your account, please reach out to [support@worldlabs.ai](mailto:support@worldlabs.ai).
# FAQ
Source: https://docs.worldlabs.ai/marble/support/faq
Common questions and answers about Marble!
### How can I insert characters?
Currently, this can be done by exporting to Spark or other software. Learn more here: [https://t.co/r4Dia8P7c8](https://t.co/r4Dia8P7c8)
### Is there an API available?
Yes! See [platform.worldlabs.ai](https://platform.worldlabs.ai) for more information.
### How do I share my world with others?
Click on your world card to open the preview, then use the "Copy link" button to share a web link that others can view in their browser.
### How do I experience the world in VR?
Open your world card and use the "Copy VR link" button to get a VR-compatible link, or click the VR icon to open the world directly in your VR headset.
### Where can I read more about World Labs policies?
Please view our [Terms of Service](/terms-of-service) and [Privacy Policy](/privacy-policy) for details.
# Support & feedback
Source: https://docs.worldlabs.ai/marble/support/support-feedback
Join our community, get support, and share feedback to help shape the future of Marble.
## Join our community
In addition to the learning resources found here, you can also join the World Labs
[Discord](https://discord.gg/jSSSgXWT3v) server to connect with other creators and get
more information. We announce product updates, listen to your feature requests, and
even host live events. It's where our creators from around the world come together to
brainstorm ideas and showcase the latest projects they're working on in Marble.
Want more? Check out our [YouTube](https://www.youtube.com/@WorldLabsAI) channel and
follow us on [X](https://x.com/theworldlabs) and
[Instagram](https://www.instagram.com/theworldlabs/?igsh=NTc4MTIwNjQ2YQ%3D%3D#) to stay
up to date.
## Get support & share feedback
Have questions or feedback? Head over to the 🤝│ help channel in
[Discord](https://discord.gg/jSSSgXWT3v) to get support and the 💡 | ideas-and-features
channel to report a bug or share your feature ideas. We welcome all feedback to help us
shape the future of Marble!
We look forward to seeing the worlds you create!
## Review World Labs policies
Looking for more details? View our [Terms of Service](/terms-of-service) and [Privacy Policy](/privacy-policy).