agentleFS
decentralanddecentraland/sdk-skills/deploy-scene/SKILL.md

deploy-scene

Skill4 starsChanged 9 days agoApache-2.0View on GitHub

Deploy a Decentraland scene to Genesis City (LAND-based). Use when the user wants to deploy or publish to parcels they own, reduce the deployed scene size, upload a large scene, script or cancel a deploy, or hits a DEPLOY_* error. Do NOT use for Worlds deployment (see deploy-worlds).

  • Deletes or force-pushes
  • Installs packages

The file

We split this skill into the rules it gives an agent

5 rules. About 0 are common, 1 is distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Run npx @dcl/sdk-commands start to verify the scene works (add --web to preview in the Bevy Web browser client).

    line 37
  2. Distinctive:

    Read the square image. Done when every word of the text and the main subject are inside it, the ratio is 16:9 and the file is under 1 MB.

    line 140
  3. Very specific:

    If a deploy fails with "Scene is too large", checking .dclignore is the first step: working files are often the bulk of the excess, and …

    line 215
  4. Very specific:

    When an asset silently fails to load or play in preview and .dclignore has !

    line 222
  5. Very specific:

    After every publish, the content servers compress all .gltf/.glb models to asset bundles — a significantly lighter format.

    line 228

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Deploying to Genesis City

Deploy to specific parcels you own or have permission to deploy to.

Deploy with npx @dcl/sdk-commands deploy (or the Creator Hub's Publish button). It builds the scene, uploads it to IPFS and asks a wallet that owns the parcels (or has deploy permission) to sign.

Pre-Deployment Checklist

Before deploying, verify:

  1. scene.json is valid:

    • ecs7: true and runtimeVersion: "7"
    • Correct parcels matching your LAND (for Genesis City)
    • Valid base parcel
    • main: "bin/index.js"
  2. Discovery metadata is complete — these four fields decide whether anyone finds and enters the scene, and they are frequently left at template defaults. Check each one and fill in what you can infer from the scene itself; ask the user only for what you can't:

    • display.title — the scene name, shown under the minimap in-world and in the map modal
    • display.description — one or two sentences on what the scene is. Aim for ~140 characters that say what the player does there (verb first, who with, why come back). It is the line on the Places card that earns the tap (Foundation marketing requirements)
    • tags — root-level array, 1-3 Places-dApp categories inferred from the scene's theme. Valid values: {baseDir}/../create-scene/references/scene-json.md → Tags (scene categories)
    • display.navmapThumbnail — set, and the image passes every check in Thumbnail image below
  3. Code compiles:

    npx tsc --noEmit
  4. Scene previews correctly: Run npx @dcl/sdk-commands start to verify the scene works (add --web to preview in the Bevy Web browser client). Test with multiple browser tabs to verify multiplayer behavior.
  5. Dependencies installed:

    npm install
  6. Assets are within limits — see the optimize-scene skill for full limit formulas per parcel count (triangles, entities, materials, textures) and the flat 330 m height limit. Keep scene load time under 15 seconds by optimizing assets.

    Then do a size pass on what will actually upload, before the deploy rather than after a rejection:

    find assets -type f -size +1M -exec ls -lh {} +        # the heavy files
    npx @gltf-transform/cli inspect assets/Models/big.glb   # Textures table: each embedded map's resolution and bytes

    Embedded 2048 px texture maps are the usual surprise — two generated GLBs carried 17 MB of them in a 34 MB scene. Resize them to 1024 or less (npx @gltf-transform/cli resize in.glb out.glb --width 1024 --height 1024, or in Blender — see optimize-scene): the converter downscales to 1024 anyway, and the raw file still counts in full. Estimate code size from a production build, not from the project's bin/index.js: the preview's dev build carries an inline source map and no minification, while deploy rebuilds with --production (minified, no map), typically a fraction of the size.

  7. .dclignore covers all working files — Blender/FBX sources, concept art, spreadsheets, markdown docs, etc. must not be uploaded. See the .dclignore section below.

Deployment Process

Using CLI

# Build first
npx @dcl/sdk-commands build

# Deploy (will open browser for wallet connection)
npx @dcl/sdk-commands deploy

Scenes of 50 MiB or more upload in 32 MiB batches (@dcl/sdk-commands 7.31.0+), printing Uploaded X MB of Y MB (batch n/N); smaller ones go in one request. The linker page shows the upload progress and can cancel it. Batch rules, every flag, the /api/deploy API for scripting, following or cancelling a deploy, and the DEPLOY_* error codes: {baseDir}/references/deploy-cli.md.

Using Creator Hub

  1. Open Creator Hub
  2. Select your scene
  3. Click "Publish"
  4. Connect wallet
  5. Confirm transaction

The link to share after publishing

The success panel shows and copies a web /jump link, which works for players without the desktop client; its Jump In button opens the desktop client separately. Use the same forms in anything you write for players:

TargetShare link
Genesis City scenehttps://decentraland.org/jump?position=x,y
Worldhttps://decentraland.org/jump?realm=NAME.dcl.eth
Desktop client directlydecentraland://?position=x,y&dclenv=<env> (or ?realm=…)

/jump is canonical — the legacy /play/... paths only 301-redirect to it, so do not generate them.

scene.json for Deployment

{
  "ecs7": true,
  "runtimeVersion": "7",
  "display": {
    "title": "My Awesome Scene",
    "description": "A description for the marketplace",
    "navmapThumbnail": "assets/Images/thumbnail.png"
  },
  "tags": ["art", "social"],
  "scene": {
    "parcels": ["0,0", "0,1"],
    "base": "0,0"
  },
  "main": "bin/index.js"
}

tags is root-level, not under display. Every field: {baseDir}/../create-scene/references/scene-json.md.

Thumbnail image

display.navmapThumbnail is the image players see in the map modal when they select the scene's parcels, and in the confirmation screen when another scene teleports them there. Always provide one.

Spec:

  • .png or .jpg, 16:9 within 1 % (the Creator Hub validates this), recommended 1920x1080 px (1344x756 for a lighter file). Other sizes work as long as they keep 16:9 — anything else is stretched
  • Central square safe area: parts of the platform show a square crop of the central 1080x1080 px, cutting ≈ 0.219 of the width off each side (x 420–1500 at 1920 wide, x 280–1000 at 1280 wide). Keep text, logos and the main subject inside it
  • Under 1 MB (1,048,576 bytes) — the Worlds content server rejects anything larger at deploy time (Invalid thumbnail: size … exceeds maximum of 1048576 bytes (1MB)), and a plain 1920x1080 PNG can exceed it. Nothing local warns you. For a generated cover (Blender render, image tool), export JPEG at high quality: ffmpeg -i cover.png -q:v 2 cover.jpg came out around 100 KB at 1344x756. Keep a PNG only if it stays under the cap
  • Best practice: a gameplay screenshot with a clear, distinctive logo overlaid and minimal text, still readable at phone-card size
  • Value is a path inside the project (e.g. assets/Images/thumbnail.png) or a URL to an externally hosted image — an external host must serve permissive CORS headers
  • Save a custom cover under its own name (e.g. assets/Images/cover.png). The Creator Hub overwrites assets/Images/autogenerated-thumbnail.png with a viewport capture whenever it auto-generates a thumbnail, including right before a publish

Check before pointing navmapThumbnail at an image, whether you captured, rendered or composed it:

  1. Byte size and dimensions: ls -l <path>, then sips -g pixelWidth -g pixelHeight <path> (macOS) or magick identify -format "%wx%h\n" <path>.
  2. Crop the central square and look at that, not at the full frame (a title running edge to edge looks fine in the full frame):

    OUT="${TMPDIR:-/tmp}/thumb-square.png"
    # macOS
    H=$(sips -g pixelHeight <path> | awk '/pixelHeight/{print $2}'); sips -c $H $H <path> --out "$OUT"
    # ImageMagick (any OS)
    H=$(magick identify -format %h <path>); magick <path> -gravity center -crop ${H}x${H}+0+0 +repage "$OUT"
  3. Read the square image. Done when every word of the text and the main subject are inside it, the ratio is 16:9 and the file is under 1 MB. Otherwise recapture or re-render.

Capture it yourself — ask the user if instead of providing an image they prefer you to capture it With the unity-explorer MCP (see the unity-explorer-mcp skill), run the scene in preview, move the camera to frame a shot that shows what the scene is about — composing so the essential content sits in the middle third, inside the central square — and capture a UI-less PNG with the bundled script:

# <skill-dir> is the unity-explorer-mcp skill's base directory
<skill-dir>/scripts/screenshot.sh --world-only --png -w 1920 -o assets/Images/thumbnail.png

Launching the client windowed at 16:9 (npm run start -- --mcp -- --windowed-mode --resolution 1920x1080) makes the frame 16:9 natively, so no crop is needed. Otherwise normalize it:

# ImageMagick: center-crop to 16:9 and resize, in one step
magick assets/Images/thumbnail.png -resize 1920x1080^ -gravity center -extent 1920x1080 assets/Images/thumbnail.png

# macOS, no extra tooling: -c takes HEIGHT then WIDTH, both centered
sips -g pixelWidth -g pixelHeight assets/Images/thumbnail.png   # read the source size first
sips -c 1080 1920 assets/Images/thumbnail.png --out assets/Images/thumbnail.png   # center-crop a larger frame
sips -z 1080 1920 assets/Images/thumbnail.png                              # then resample to exactly 1920x1080

Point display.navmapThumbnail at the resulting path. Only if the MCP is unavailable, ask the user for an image rather than shipping the scene without one. Make sure .dclignore doesn't exclude the thumbnail — it must be uploaded with the scene.

Letter case matters. The path in navmapThumbnail must match the folder on disk exactly: the deploy file list preserves on-disk casing and the Catalyst matches case-sensitively, while macOS and Windows resolve either casing locally — so a mismatch publishes as Scene thumbnail '…' must be a file included in the deployment and never shows up in testing. In a Creator Hub project the standard folder is assets/Images/ (capital I), which is also where the Hub writes its own autogenerated capture. ls assets before writing the path, and never keep two folders differing only in case.

Spawn Points

Spawn points: {baseDir}/../create-scene/references/scene-json.md → Spawn Points.

.dclignore — Exclude Files from Upload

The .dclignore file, always at the project root, lists files and patterns that are NOT uploaded to the content server when deploying, and not served by the local preview either (see .dclignore also filters the preview below). Everything in the project folder that isn't matched by .dclignore gets uploaded, and the uploaded total counts against the per-parcel MB limits — so only files the running scene actually needs should be deployed.

Format: one glob pattern per line. The default from scene templates:

.*
package-lock.json
yarn-lock.json
build.json
export
tsconfig.json
tslint.json
node_modules
*.ts
*.tsx
.vscode
Dockerfile
dist
README.md
*.blend
*.fbx
*.zip
*.rar
*.md
src

Keep it up to date as the project grows. Whenever working files exist in the project — Blender or other 3D source files, draft models, concept art, PSDs, spreadsheets, markdown notes, reference photos — add them (or their extensions) to .dclignore so the deployed scene stays as light as possible. When creating or editing a scene, add these patterns proactively; don't wait for the deploy to fail. Common additions:

*.blend
*.blend1
*.fbx
*.psd
*.kra
*.xcf
*.md
*.csv
*.xlsx
drafts
concept-art
reference

If a deploy fails with "Scene is too large", checking .dclignore is the first step: working files are often the bulk of the excess, and excluding them reduces upload size with zero impact on the scene.

.dclignore also filters the preview

npm run start builds the preview's scene content list from the same publishable-file set as deploy (getProjectPublishableFilesWithHashes in @dcl/sdk-commands, verified in source), so an excluded asset does not exist for the running scene. The client never requests it: no 404, no log line. An excluded _emote.glb makes triggerSceneEmote resolve false and the emote simply does not play (observed twice, 2026-11, with a whitelist-style .dclignore that re-included only some files under assets/animations/).

  • Exclude by pattern — working-file extensions and folders, as above — and let every asset folder upload by default. A whitelist (* plus ! re-includes) drops each new asset until someone edits the list.
  • When an asset silently fails to load or play in preview and .dclignore has ! lines, grep it for the asset's path before debugging anything else.

Keep these uploaded: bin/index.js, scene.json, assets/ (composites, .glb models, textures, sounds, video), the thumbnail referenced in scene.json, and any file path referenced in code. The default ignores *.ts/src because only the compiled bin/index.js runs.

Post-Publish: Asset Bundle Conversion

After every publish, the content servers compress all .gltf/.glb models to asset bundles — a significantly lighter format. The conversion starts immediately but is queued per platform (Windows, Mac). While it runs, players are deliberately served the last fully-working version of the scene.

  • Typical time: seconds. Longer for very large scenes or when the conversion servers are busy.
  • A conversion running more than a couple of minutes is a failure signal, not normal queuing — check the status endpoint rather than continuing to wait.
  • The Jump In button appears as soon as the scene is playable, once upload and conversion are done.
  • Before a live event: publish your final version at least one hour in advance, to leave room for anything unexpected. Avoid republishing while waiting — each publish restarts the queue.
  • Check conversion status in a browser:
    • https://asset-bundle-registry.decentraland.org/entities/status/<pointer> — replace <pointer> with a scene coordinate (e.g. 20,-34) or the deployment entity ID. Shows per-platform status under assetBundles and LOD status under lods. For a World, append ?world_name=myname.dcl.eth.
    • https://asset-bundle-registry.decentraland.org/queues/status — lists all scenes currently queued for conversion, per platform.
  • /detectabs chat command: in-world, tints models green (converted) or red (not yet converted).
  • Reloading the scene is not enough to pick up a new version — reload restarts the scene's code but doesn't fetch newly published content. Any player who already loaded the scene this session keeps seeing the cached version until they fully close and re-enter Decentraland. That includes you: after conversion completes, quit and relaunch, then re-enter via jump link or /goto.

The desktop preview already runs the same conversion locally, so catch conversion issues before publishing in its AB panel — see the optimize-scene skill ("Local Asset Bundle Preview").

Troubleshooting

ErrorCauseSolution
"You don't have permission to deploy"Wallet doesn't own the target LAND/parcelsVerify LAND ownership on the marketplace, or get deployment permissions from the LAND owner
"Scene is too large"Assets exceed parcel size limitsFirst add all working files (Blender/FBX sources, concept art, docs) to .dclignore — see the .dclignore section above. Then check triangle count, file sizes, and texture counts against the limits. See optimize-scene skill
Wallet connection failsBrowser popup blocked or MetaMask lockedAllow popups, unlock MetaMask, refresh and try again
"Invalid scene.json"Missing required fields or malformed JSONVerify ecs7: true, runtimeVersion: "7", valid parcels array, and main: "bin/index.js"
Deploy succeeds but scene is emptymain field doesn't point to compiled outputEnsure main is "bin/index.js" and run npx @dcl/sdk-commands build first
Catalyst rejectionContent violates Decentraland content policiesReview content guidelines at docs.decentraland.org
Scene looks broken right after deployAsset bundle conversion not done yetType /detectabs in chat — red-tinted models are not yet converted. Check conversion status (see above) and wait
Some players see old version, others see newPer-platform conversion finishes at different times + client cachingCheck both windows and mac under assetBundles in the conversion status endpoint. Once both are complete, affected players must fully restart Decentraland
Publication stuck on Converting stageConversion failed, or the scene is queued behind othersConversion normally takes seconds, so more than a couple of minutes points at a failure rather than a queue. Check the queue status URL for your entity ID. If not queued, check conversion status — if a platform shows failed, republish. If it fails again, report the bug with the entity ID
3D models missing, black, or untextured after deployConversion still in progress, or textures exceed the conversion cap/detectabs to check; textures in 3D models are capped to 1024x1024 during conversion
Scene looks fine up close but broken from a distanceLOD generation (final publish stage) not done yetCheck the lods values in the conversion status endpoint; LODs don't block close-range testing
"Scene thumbnail '…' must be a file included in the deployment" (the file plainly exists)Letter-case mismatch between display.navmapThumbnail and the folder on disk (e.g. assets/images/ vs assets/Images/)ls assets and make the path match the folder exactly — see Thumbnail image → Letter case
"Invalid thumbnail: size … exceeds maximum of 1048576 bytes"Thumbnail over 1 MB (a plain 1920x1080 PNG can be)Export JPEG or shrink to 1344x756 — see Thumbnail image
DEPLOY_PARTIAL_NOT_SUPPORTEDThe server can't take batched uploads and the content it is missing is over 200 MBShrink the scene (.dclignore, texture sizes) or deploy to a server that supports batches — {baseDir}/references/deploy-cli.md
DEPLOY_CANCEL_OUTCOME_UNCERTAINA cancel landed mid-upload and the CLI couldn't tell whether the server published itCheck the scene (conversion status URL above) before deploying again
tsc/deploy build fails on node_modules/@dcl/js-runtime/sdk.d.ts importing a script file that no longer existsThe bundler rewrites that file's ~sdk/script-utils declaration only while the composite still has at least one script; removing the last Creator Hub script asset leaves the old declaration pointing at the deleted .tsxDelete the declare module '~sdk/script-utils' block at the end of sdk.d.ts (or rm -rf node_modules/@dcl/js-runtime && npm install), then build again. Keeping one script in the scene avoids it

Genesis City vs Worlds

Genesis CityWorlds
RequirementOwn LAND parcelsOwn DCL NAME or ENS domain
Parcel limitsEnforced (entity/triangle budgets per parcel)Not constrained by LAND
VisibilityShown on the Genesis City mapListed on Places page (opt-out available)
Deploy targetDefault Catalyst network--target-content https://worlds-content-server.decentraland.org
Best forPermanent installations, high-traffic scenesTesting, personal spaces, events

To deploy to a World, use deploy-worlds.

Scene Tipping

To let visitors tip the scene creator in MANA, set the root-level creator wallet address in scene.json: {baseDir}/../create-scene/references/scene-json.md → Fields.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.