Skip to content

Render tools

Environment, effects, hair, shaders, render layers, impostors, benchmarks and the movie renderer.

27 tools in the render category. Badges: read-only tools never change the project; mutating tools are undoable and attributed; open-world tools reach outside the engine and MCP clients ask before running them.

Tool Summary
environment_get Current sun, sky, ambient, fog, exposure and grid settings.
environment_update Edit scene lighting/atmosphere: sun (azimuth, elevation, color, intensity), sky colors, ambient, fog, exposure, grid.
shader_get The renderer's current shader source (Metal Shading Language).
shader_set Replace the renderer's shader source at runtime.
perf_stats Frame cost and scene complexity: GPU and CPU frame time, draw calls, lights, terrain nodes, foliage instances, entities, behaviors and assets.
fx_create Create an effect or a body of water from a preset, ready to tweak.
fx_burst Emit particles from an emitter right now (explosions, muzzle flashes, impacts).
groom_create Grow strand hair or fur on an entity's mesh (a head, a bust, an animal body) from a preset, ready to tweak.
groom_update Change a groom's fields (or switch to another preset and then apply fields).
groom_info Strand, guide and point counts, memory (CPU and GPU), generation time and the measured GPU cost (simulation and drawing) of a groom, or of every groom in the scene when entity is omitted.
fx_benchmark Render frames real-time frames back to back (no readback, effects time advancing 1/60 s per frame, so GPU particles and hair simulate) and report the GPU time per frame and per effects pass, plus the wall-clock time.
fx_stats Measured GPU cost of the last rendered frames: whole-frame GPU time, the GPU simulation pass (particles + hair), per-emitter live particle counts (read back asynchronously, a few frames old) and per-groom sizes.
impostor_bake Bake the octahedral impostors of foliage layers now: each heavy model (an imported tree, bush, rock or grass clump; one mesh or every part of a prefab) is captured from up to 32x32 directions into an atlas of albedo, normal and depth, and instances beyond the layer's transition distance render as impostors (lit, shadowed and depth-correct like real geometry).
movie_render Render a cinematic to video or a PNG sequence, offline and deterministically (the movie renderer).
render_layers Render layers (20): MeshRenderer.layers says which layers a mesh is on; Camera.cullMask and Light.cullMask say which layers a camera draws and a light illuminates (a mesh is drawn / lit when they share a layer).
shadow_atlas_info Which point and spot lights cast shadows this frame and how: projection (spot view, cube = 6 views, dual_paraboloid = 2 views), atlas slot and resolution, whether it was re-rendered this frame or reused from the static cache, and why a light has no shadow (disabled, out_of_view, beyond_max_distance, over_light_budget, atlas_full, pending).
light_shadows Turn shadows of point/spot lights on or off and tune them, for one light, a list, or every light ("all", optionally only one kind).
probe_add Add a reflection probe: a captured cubemap that glossy floors, metal and glass inside its volume reflect instead of the sky (box-projected, so reflections line up with the walls), and that gives them their ambient light.
probe_bake Re-capture reflection probes now (after moving furniture, changing lights or the sky): renders one still frame of view, which captures every invalidated probe in full regardless of the per-frame face budget, and returns their state (slot, captures, GPU time) plus warnings.
probe_info Reflection probes and their atlas: the budget (Environment.probeBudget probes, probeUpdates faces per frame), atlas resolution / slots / memory, and per probe: slot and debug color (the reflection_probes debug view draws it), whether it is captured, lighting this view, waiting for the face budget, over budget or out of view, captures so far, last capture GPU time, stale (something in range changed since a once probe was captured: run probe_bake), size, update mode.
particles2d_create Create pixel-art particles for a 2D game from a preset, ready to tweak: rain (streaks over the whole view), drizzle, snow (drifting flakes), leaves and petals (falling, swaying), fireflies (glowing, twinkling, wandering), smoke (chimney puffs), ripples (puddle rings while it rains), dust (motes in sunlight), sparkle (water glints).
particles2d_info Live state of particles2d emitters: particles alive, cap, rate, box, wrap, and the bounds the live particles cover (world units).
locale_list Lists the game's locales: the current one and its fallback chain (pt-BR -> pt -> source), the source language, every locale with strings (count, coverage, files under locale/), game.json localization settings, strings requested at run time that no table has, and table file problems.
locale_set Switches the language the game shows: texts written as "@key" (ui.text, ui.placeholder, text.text), tr() calls and #line dialogue lines follow at once.
locale_check Audits localization and returns findings an agent can fix one by one: per locale the keys with no translation (and coverage); keys the project uses ("@key" fields, tr("key") in scripts, #line dialogue ids) that no table defines; unused keys; placeholder mismatches between a translation and the source ({name} missing or extra); invalid messages; plurals missing forms the language needs (ru: one/few/many, ar: zero..many); strings longer than their budget (the table's max column, or source length x budget, default game.json maxLengthRatio); characters no font covers (CJK, Arabic...: add a font); keys requested at run time but missing.
locale_extract Scans the open scene, scenes, prefabs, dialogue scripts and Wander code for hard-coded player-facing texts (ui.text, ui.placeholder, text.text, dialogue lines and choices without a #line id, .text = "..." in scripts) and proposes a key for each ("main_menu.play", "dlg.intro.start.003"); a text whose source string already exists reuses its key.
locale_pseudo Writes a pseudo-locale for layout testing: every string accented and about 30% longer inside brackets ("Play" -> "[Ƥļáý~~]"), placeholders, plurals and rich-text tags intact, so truncation, overflow and hard-coded texts (they stay plain) jump out.

environment_get

Get lighting & environment read-only

Current sun, sky, ambient, fog, exposure and grid settings.

Takes no arguments.

environment_get {}
skywalker call environment_get '{}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "environment_get",
    "arguments": {}
  }
}

environment_update

Lighting & environment mutating

Edit scene lighting/atmosphere: sun (azimuth, elevation, color, intensity), sky colors, ambient, fog, exposure, grid. Optional preset: noon, sunset, night, overcast, studio (applied first).

Argument Type Required Description Values
skyTop color Sky color at the zenith — hex string "#rrggbb[aa]" or [r,g,b(,a)] in 0..1
skyHorizon color Sky color at the horizon — hex string "#rrggbb[aa]" or [r,g,b(,a)] in 0..1
ground color Ambient bounce color from below — hex string "#rrggbb[aa]" or [r,g,b(,a)] in 0..1
ambient number Ambient light strength 0 .. 10
sunAzimuth number Sun compass direction in degrees (0 = +Z)
sunElevation number Sun height above horizon in degrees -90 .. 90
sunColor color Sun light color — hex string "#rrggbb[aa]" or [r,g,b(,a)] in 0..1
sunIntensity number Sun brightness 0 .. 100
fogColor color Distance fog color — hex string "#rrggbb[aa]" or [r,g,b(,a)] in 0..1
fogDensity number Exponential fog density (0 = off) 0 .. 1
exposure number Camera exposure multiplier 0.01 .. 20
autoExposure boolean Adapt exposure to scene brightness (eye adaptation)
exposureCompensation number Exposure compensation in EV stops -6 .. 6
adaptationSpeed number Auto exposure adaptation speed 0.05 .. 20
showGrid boolean Draw the editor ground grid
bloomIntensity number Glow around bright/emissive things (0 = off) 0 .. 5
bloomThreshold number Brightness where glow starts (lower = more glow) 0 .. 10
bloomClamp number Brightest (exposed) value that feeds the glow: 2-8 keeps sun glints, chrome and LED lights crisp instead of blown-out halos; 64 = effectively unlimited 0.1 .. 64
saturation number Color saturation (1 = neutral) 0 .. 2
contrast number Contrast (1 = neutral) 0.5 .. 2
vignette number Darken the image corners 0 .. 1
grain number Film grain 0 .. 1
chromaticAberration number Lens color fringing toward the frame edges 0 .. 1
look string Color grading look none warm cool teal_orange golden_hour bleach noir vivid moonlight vintage
lut string Optional .cube 3D LUT file (project-relative); when set it replaces the look (lookStrength blends it)
lookStrength number Strength of the look / LUT 0 .. 1
skyMode string gradient = two artist colors; atmosphere = physically inspired sky from the sun; hdri = a photographed .hdr panorama lights and backs the scene (set hdri) gradient atmosphere hdri
hdri string Equirectangular .hdr panorama, project-relative (skyMode hdri)
hdriRotation number Panorama rotation in degrees (line its sun up with sunAzimuth)
hdriIntensity number Panorama brightness 0 .. 20
clouds number Procedural cloud cover 0 .. 1
cloudMode string Clouds: volumetric (ray-marched, lit, shadows) or flat (cheap) volumetric flat
cloudHeight number Cloud layer base height in meters 100 .. 12000
cloudThickness number Cloud layer thickness in meters 100 .. 10000
cloudDensity number Cloud density: 0.3 wispy .. 2 stormy 0 .. 4
cloudScale number Cloud feature size (0.5 small puffs .. 3 huge banks) 0.1 .. 8
cloudSpeed number Cloud drift speed in m/s (along windDirection) 0 .. 100
stars number Stars in the night sky 0 .. 1
sunSize number Sun / moon disc size 0.1 .. 8
fogHeight number Fog pools near the ground as this grows (0 = uniform) 0 .. 2
reflections number Sky reflections / image-based lighting strength 0 .. 3
ao number Ambient occlusion (contact shadows in corners and creases) 0 .. 2
aoRadius number Ambient occlusion radius in meters 0.05 .. 5
gi number Screen-space global illumination: bounce and emissive light (0 = sky light only) 0 .. 1
giDistance number Global illumination ray length in meters 0.5 .. 50
ssr number Screen-space reflections on glossy surfaces (wet streets, floors, metal) 0 .. 1
taa boolean Temporal anti-aliasing (smooth edges, stable shimmer-free detail)
renderScale number Real-time internal resolution: 0.5-0.77 renders fewer pixels and MetalFX temporal upscaling reconstructs full resolution (faster); 1 = native. Stills and captures with samples > 1 render native. 0.33 .. 1
sharpen number Sharpening after temporal anti-aliasing 0 .. 1
shadowSoftness number Sun shadow penumbra size 0 .. 6
tonemap string HDR to display curve aces agx neutral filmic none
temperature number White balance: -1 cool .. +1 warm -1 .. 1
tint number White balance: -1 green .. +1 magenta -1 .. 1
shadowDistance number Sun shadow range in meters (0 = automatic) 0 .. 5000
godRays number Volumetric light: sun shafts through trees and windows, cones under lamps (0 = off, 1 = natural) 0 .. 8
haze number Air density for volumetric light, extinction per meter: 0.0003 clear landscape, 0.002 hazy valley, 0.01-0.03 misty alley or interior 0 .. 1
windSpeed number Wind (m/s) that carries smoke, rain, snow and particles 0 .. 60
windDirection number Direction the wind blows toward (degrees, 0 = +Z)
localShadowAtlas integer Point/spot shadow atlas size in px: 2048 (16 MB), 4096 (64 MB, default), 8192 (256 MB)
localShadowLights integer Most point/spot lights with shadows per frame (the most important on screen); the rest light without shadows. 0 = no local shadows
localShadowUpdates integer Point/spot shadow views re-rendered per frame when lights or casters move (cube light = 6, spot = 1); later updates wait a frame. Stills render all
probeBudget integer Reflection probes with a slot in the probe atlas (the most important in view first; the rest fall back to the sky). Max 32; 0 = probes off
probeUpdates integer Reflection probe cube faces captured per frame (a whole probe = 6); later captures wait a frame. Stills capture everything
preset string Lighting preset noon sunset night overcast studio
align_sun_to_hdri boolean Point the sun (direction of light and shadows) at the brightest spot of the hdri panorama
environment_update {"preset": "overcast", "skyMode": "atmosphere", "clouds": 0.85, "cloudMode": "volumetric", "cloudDensity": 1.3, "sunIntensity": 1.2, "shadowSoftness": 4, "ao": 1.2, "aoRadius": 1.0, "gi": 0.5, "fogColor": "#9aa6b2", "fogDensity": 0.006, "fogHeight": 0.5, "saturation": 0.85, "look": "bleach", "lookStrength": 0.25, "tonemap": "agx", "autoExposure": true}
skywalker call environment_update '{"preset": "overcast", "skyMode": "atmosphere", "clouds": 0.85, "cloudMode": "volumetric", "cloudDensity": 1.3, "sunIntensity": 1.2, "shadowSoftness": 4, "ao": 1.2, "aoRadius": 1.0, "gi": 0.5, "fogColor": "#9aa6b2", "fogDensity": 0.006, "fogHeight": 0.5, "saturation": 0.85, "look": "bleach", "lookStrength": 0.25, "tonemap": "agx", "autoExposure": true}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "environment_update",
    "arguments": {
      "preset": "overcast",
      "skyMode": "atmosphere",
      "clouds": 0.85,
      "cloudMode": "volumetric",
      "cloudDensity": 1.3,
      "sunIntensity": 1.2,
      "shadowSoftness": 4,
      "ao": 1.2,
      "aoRadius": 1.0,
      "gi": 0.5,
      "fogColor": "#9aa6b2",
      "fogDensity": 0.006,
      "fogHeight": 0.5,
      "saturation": 0.85,
      "look": "bleach",
      "lookStrength": 0.25,
      "tonemap": "agx",
      "autoExposure": true
    }
  }
}

shader_get

Get shader source read-only

The renderer's current shader source (Metal Shading Language). Edit it with shader_set.

Takes no arguments.

shader_get {}
skywalker call shader_get '{}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "shader_get",
    "arguments": {}
  }
}

shader_set

Hot-reload shaders mutating

Replace the renderer's shader source at runtime. On a compile error nothing changes and the compiler diagnostics are returned. Function names and struct layouts must be kept.

Argument Type Required Description Values
source string yes Complete shader source
shader_set {"source": "// the full Metal Shading Language source from shader_get, edited"}
skywalker call shader_set '{"source": "// the full Metal Shading Language source from shader_get, edited"}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "shader_set",
    "arguments": {
      "source": "// the full Metal Shading Language source from shader_get, edited"
    }
  }
}

perf_stats

Performance stats read-only

Frame cost and scene complexity: GPU and CPU frame time, draw calls, lights, terrain nodes, foliage instances, entities, behaviors and assets. Pass frames (e.g. 30) to benchmark real-time rendering of the current view at width x height (temporal AA, no supersampling) and get average/min/max GPU ms. Check after big scatters, generators or look changes; 16.6 ms = 60 fps. view is "editor", "scene" or a custom camera {eye, target, fov}; quality benchmarks an editor viewport tier. The gpu section includes foliage impostor stats (impostorInstances, meshInstances, impostorsBaked, impostorBakeMs). passes=true adds where the frame time goes: profile.passes = GPU time per render pass (Main, Shadow cascades, SSGI, SSR, Lighting resolve, TAA, Bloom, Composite, Foliage cull, GPU particles, Hair ...; rolling 60-frame avg/min/max), profile.groups = the same summed by area (shadows, main, ao, ssgi, ssr, resolve, effects, volumetrics, clouds, temporal, post, foliage, particles, hair, ui ...) and profile.cpu = CPU scopes (frame.build, scene.buildFrame, world.gather, render.encode ...). With frames > 0 the profile covers exactly the benchmark frames. Example: {"frames":30,"passes":true,"view":{"eye":[0,300,-600],"target":[0,80,0]}}.

Argument Type Required Description Values
frames integer Benchmark this many real-time frames first (default 0 = just report)
width integer Benchmark width (default 1920)
height integer Benchmark height (default 1080)
view any Camera for the benchmark: "editor" (default), "scene", or
quality string Viewport quality tier to benchmark (default full, as in play mode and captures) full balanced fast
passes boolean Add the per-pass GPU timeline and CPU scopes (profile: passes, groups, cpu)
characters boolean With frames: also benchmark with hair and fur hidden to measure what grooms cost to draw (characters.groomRenderGpuMs)
perf_stats {"frames": 30, "passes": true, "view": {"eye": [0, 300, -600], "target": [0, 80, 0]}}
skywalker call perf_stats '{"frames": 30, "passes": true, "view": {"eye": [0, 300, -600], "target": [0, 80, 0]}}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "perf_stats",
    "arguments": {
      "frames": 30,
      "passes": true,
      "view": {
        "eye": [
          0,
          300,
          -600
        ],
        "target": [
          0,
          80,
          0
        ]
      }
    }
  }
}

fx_create

Create effect mutating

Create an effect or a body of water from a preset, ready to tweak. Volumetric fluid simulations (GPU, ray-marched, film-quality fire and smoke): volume_fire, volume_torch, volume_smoke, steam_vent, explosion_volume. Particles: fire (sprite), embers, smoke, steam, sparks, rain, snow, mist, spray, dust, fireflies, magic, fireball, debris_smoke, shrapnel. GPU particles (millions, depth collisions, sub-emitters, ribbons, mesh particles; visuals only): sparks_shower, fireworks, rain_heavy, waterfall_mist (composites), ember_storm, magic_vortex, dust_storm, falling_leaves (~6 m up), snow_heavy (~12 m up), smoke_column_gpu. Composites (volumetric fire + embers + light): campfire, torch, burning_barrel, explosion; sprite_campfire is the cheap particle version. Water (FFT ocean simulation): ocean, calm_sea, storm, lake, pool, puddle — the entity's y is the water level. overrides patches the particles/water fields (e.g. {"rate": 80, "colorStart": "#7af"}). Fires light the scene by themselves; rain/snow cover a box around the entity (place it ~12 m up), set floorHeight to the ground.

Argument Type Required Description Values
effect string yes Preset fire embers smoke steam sparks rain snow mist spray dust fireflies magic fireball debris_smoke shrapnel ember_storm magic_vortex dust_storm falling_leaves snow_heavy smoke_column_gpu sparks_gpu embers_gpu rain_gpu splashes_gpu rockets_gpu firework_burst_gpu mist_gpu droplets_gpu volume_fire volume_torch volume_smoke steam_vent explosion_volume campfire torch explosion burning_barrel sparks_shower fireworks rain_heavy waterfall_mist sprite_campfire ocean calm_sea storm lake puddle pool
name string Entity name (default: the preset's name)
position number[3] World position
parent integer | string Parent entity
overrides object Fields to change on the particles/water component (composites: applied to every emitter)
fx_create {"effect": "campfire", "name": "Camp Fire", "position": [0, 0, 0]}
skywalker call fx_create '{"effect": "campfire", "name": "Camp Fire", "position": [0, 0, 0]}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "fx_create",
    "arguments": {
      "effect": "campfire",
      "name": "Camp Fire",
      "position": [
        0,
        0,
        0
      ]
    }
  }
}

fx_burst

Burst particles read-only

Emit particles from an emitter right now (explosions, muzzle flashes, impacts). Works while editing (preview) and playing. In Wander use burst(n).

Argument Type Required Description Values
entity integer | string yes Entity with a particles component (or a composite whose children have one)
count integer Particles to emit (default: the emitter's burst field, else 50)
fx_burst {"entity": "Camp Fire", "count": 60}
skywalker call fx_burst '{"entity": "Camp Fire", "count": 60}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "fx_burst",
    "arguments": {
      "entity": "Camp Fire",
      "count": 60
    }
  }
}

groom_create

Create hair or fur mutating

Grow strand hair or fur on an entity's mesh (a head, a bust, an animal body) from a preset, ready to tweak. Presets: hair_straight, hair_wavy, hair_curly, hair_ponytail, hair_short (heads: they grow on the upper back of the mesh; set maskDirection/maskAngle or a vertex-color mask for your model), fur_short, fur_long (cover the whole mesh); for rigged characters: hair_scalp (dense scalp hair on the Head bone's vertices), beard, eyebrows (regions in the head's bounds, mirrored brows), fur_dense (a creature's coat). On a rigged mesh the roots ride the animated skin (attach auto: rooted on triangles, follow, maxSpeed, capsules fitted to the skeleton as colliders); maskBone / maskCenter / maskRadius / maskSpace limit growth to a region. overrides patches groom fields, e.g. {"melanin": 0.2, "length": 0.5, "strands": 100000}. Color is physically based: melanin 0 white .. 0.3 blond .. 0.8 brown .. 1 black, redness for auburn/ginger, dye for unnatural tints. Hair is simulated (gravity, wind, collisions with the mesh) and rendered as real strands with Marschner shading and self-shadowing. Example: {"entity": "Head", "preset": "hair_wavy", "overrides": {"melanin": 0.25}}. Grooms stack: the first groom goes on the mesh entity itself, every further one (a beard and eyebrows on a scalp) on a new child entity named after the preset (or name) that grows on the same mesh and skin; replace: true replaces the existing groom instead. On a character root without a mesh, hair grows on its largest skinned part (the body). The result's entity is the groom's entity (edit it with groom_update, remove it with entity_delete). Example: {"entity": "Hero", "preset": "beard", "overrides": {"melanin": 0.6}}.

Argument Type Required Description Values
entity integer | string yes Entity with the mesh to grow on, or a character root (its body mesh)
preset string Starting style hair_straight hair_wavy hair_curly hair_ponytail hair_short fur_short fur_long beard eyebrows hair_scalp fur_dense
overrides object Groom fields to change
name string Name of the groom's own entity when it is added beside other grooms (default: the preset, e.g. "Beard")
replace boolean Replace the groom already on entity instead of adding another one
groom_create {"entity": "Head", "preset": "hair_wavy", "overrides": {"melanin": 0.25}}
skywalker call groom_create '{"entity": "Head", "preset": "hair_wavy", "overrides": {"melanin": 0.25}}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "groom_create",
    "arguments": {
      "entity": "Head",
      "preset": "hair_wavy",
      "overrides": {
        "melanin": 0.25
      }
    }
  }
}

groom_update

Update hair or fur mutating

Change a groom's fields (or switch to another preset and then apply fields). Geometry fields (strands, length, curls, clumps, mask...) regenerate the strands; color, shading and motion fields apply instantly. Example: {"entity": "Head", "fields": {"curlRadius": 0.01, "curlFrequency": 18, "melanin": 0.9}}.

Argument Type Required Description Values
entity integer | string yes Entity with a groom
preset string Reset to this preset first hair_straight hair_wavy hair_curly hair_ponytail hair_short fur_short fur_long beard eyebrows hair_scalp fur_dense
fields object Groom fields to change
groom_update {"entity": "Head", "fields": {"curlRadius": 0.01, "curlFrequency": 18, "melanin": 0.9}}
skywalker call groom_update '{"entity": "Head", "fields": {"curlRadius": 0.01, "curlFrequency": 18, "melanin": 0.9}}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "groom_update",
    "arguments": {
      "entity": "Head",
      "fields": {
        "curlRadius": 0.01,
        "curlFrequency": 18,
        "melanin": 0.9
      }
    }
  }
}

groom_info

Hair or fur info read-only

Strand, guide and point counts, memory (CPU and GPU), generation time and the measured GPU cost (simulation and drawing) of a groom, or of every groom in the scene when entity is omitted. Use it to budget hair (e.g. keep a hero head under ~2 ms at 1080p). On a character (or a mesh with several grooms) it lists every groom growing on it (grooms).

Argument Type Required Description Values
entity integer | string Entity with a groom (omit for all)
groom_info {"entity": "Hero"}
skywalker call groom_info '{"entity": "Hero"}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "groom_info",
    "arguments": {
      "entity": "Hero"
    }
  }
}

fx_benchmark

Benchmark GPU frame time read-only

Render frames real-time frames back to back (no readback, effects time advancing 1/60 s per frame, so GPU particles and hair simulate) and report the GPU time per frame and per effects pass, plus the wall-clock time. Use it to budget effects: e.g. {"width":1920,"height":1080,"frames":120}. The view is the editor camera unless eye/target are given.

Argument Type Required Description Values
width integer Width (default 1920)
height integer Height (default 1080)
frames integer Frames to render (default 120, max 2000)
eye number[3] Camera position
target number[3] Look-at point
serial boolean Wait for each frame (default true): exact per-pass GPU times; false = frames overlap like a game loop (throughput)
fx_benchmark {"width": 1920, "height": 1080, "frames": 120, "serial": true}
skywalker call fx_benchmark '{"width": 1920, "height": 1080, "frames": 120, "serial": true}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "fx_benchmark",
    "arguments": {
      "width": 1920,
      "height": 1080,
      "frames": 120,
      "serial": true
    }
  }
}

fx_stats

GPU effects stats read-only

Measured GPU cost of the last rendered frames: whole-frame GPU time, the GPU simulation pass (particles + hair), per-emitter live particle counts (read back asynchronously, a few frames old) and per-groom sizes. Render a few frames (viewport_capture) first; values are 0 on renderers without GPU effects.

Takes no arguments.

fx_stats {}
skywalker call fx_stats '{}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "fx_stats",
    "arguments": {}
  }
}

impostor_bake

Bake foliage impostors read-only

Bake the octahedral impostors of foliage layers now: each heavy model (an imported tree, bush, rock or grass clump; one mesh or every part of a prefab) is captured from up to 32x32 directions into an atlas of albedo, normal and depth, and instances beyond the layer's transition distance render as impostors (lit, shadowed and depth-correct like real geometry). The renderer bakes on first use and caches in .skywalker/cache/impostors/, so this is for baking ahead of time, rebaking after editing a mesh or material (rebake:true), or checking the atlases (preview:true returns the first atlas as an image). Tune per layer with impostorDistance (m; 0 auto from on-screen size, -1 off), impostorResolution (atlas px) and impostorFrames (views per side); see the transition distance in the result. Example: {"entity":"Forest","layer":0,"rebake":true,"preview":true}.

Argument Type Required Description Values
entity integer | string Foliage entity (default: every foliage entity in the scene)
layer any Layer index or name (default: every layer)
rebake boolean Bake again even when a cached atlas exists (default false)
preview boolean Return the first atlas as an image (default false)
save_preview string Also write that preview PNG to this project-relative path
impostor_bake {"entity": "Forest", "layer": 0, "rebake": true, "preview": true}
skywalker call impostor_bake '{"entity": "Forest", "layer": 0, "rebake": true, "preview": true}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "impostor_bake",
    "arguments": {
      "entity": "Forest",
      "layer": 0,
      "rebake": true,
      "preview": true
    }
  }
}

movie_render

Render movie read-only

Render a cinematic to video or a PNG sequence, offline and deterministically (the movie renderer). Renders a sequence (sequence: its entity or .sequence.json, whole length or start/end), an inline camera move (camera: {keys: [{t, eye, target, fov?, roll?}]} splined, or {shots: [{shot: orbit|dolly|crane|track|pan|static|path|flyover, duration, target, ...sequence_camera_shot fields}]} where each shot is a cut), or the scene camera for duration seconds. Always starts from the scene state and restores it after. simulate=true runs the game (scripts, physics); false (default) runs only sequences, animators and particles. Motion blur: shutter 0..1 of the frame (0.5 = 180 degrees; default the camera's motionBlur) accumulates sub-frames at fractional times; samples (default 8) is the per-frame budget. Outputs by extension: .mp4 = H.264 (codec hevc = 10-bit HEVC), .mov = ProRes 422 HQ, a folder or name_####.png = PNG sequence (resume=true continues an interrupted one). clay=true / debug_view="sketch" render the same move as clay or pencil sketch. Blocks until done and returns frames, timings and paths; background=true returns at once (progress: movie_progress events, action="status"; action="cancel" stops). Example: {"sequence": "Intro", "output": "renders/intro.mp4", "resolution": "1080p", "fps": 24, "shutter": 0.5, "samples": 16}.

Argument Type Required Description Values
action string render (default), status of the current/last render, or cancel it render status cancel
sequence any Sequence to render: entity with a sequencer, or a .sequence.json path
camera any Inline camera move: {keys: [{t, eye, target, fov?, roll?, ease?}]} or {shots: [{shot, duration, target, t?, ...}]}, plus aperture, focus_distance, tilt_shift
camera_entity integer | string Look through this camera instead of the scene's primary one
simulate boolean Run the full game simulation (scripts, physics) while rendering (default false)
start number Start time in seconds (default 0; the simulation pre-rolls to it)
end number End time in seconds (default: the sequence / camera path length)
duration number Seconds to render from start (instead of end)
fps integer Frames per second: 24 (default), 25, 30, 48, 50, 60
resolution string Size preset (default 1080p) 720p 1080p 1440p 4k
width integer Width in pixels (up to 3840)
height integer Height in pixels (up to 2160)
samples integer Samples per frame (default 8; 16-32 for finals): anti-aliasing, noise-free GI, motion blur
shutter number Motion blur: open shutter as a fraction of the frame (0.5 = 180-degree film look, 0 = off)
shutter_samples integer Sub-frames across the shutter (default: one per sample)
shutter_timing string Shutter interval relative to the frame time (default center) center open close
quality string Render quality tier (default full; fast for previews) full balanced fast
clay boolean Matte clay look (same move, for sketch -> clay -> final transitions)
debug_view string Buffer visualization, shading debug view (wireframe, lod, unshaded, ...) or the pencil sketch look; see viewport_capture final albedo normals material gi reflections ao depth lighting sketch impostors wireframe overdraw unshaded lighting_only shadow_cascades light_complexity lod emission specular uv_checker texel_density motion shadow_atlas reflection_probes vehicles skeleton ik_targets groom_roots sss_mask
warmup integer Frames rendered before the first one so temporal effects settle (default 4)
output string Output path: .mp4 (H.264), .mov (ProRes 422 HQ), or a folder / name_####.png (PNG sequence); default renders/<name>.mp4
outputs any[] Several outputs from one render, e.g. ["renders/a.mp4", {"path": "renders/a.mov", "codec": "prores"}]
codec string Codec for output: h264, hevc (10-bit), prores (422 HQ), prores4444, png png h264 hevc prores prores4444
bitrate_mbps number H.264 / HEVC average bitrate (default: chosen from size and fps)
resume boolean PNG sequences: continue from the first missing frame of an interrupted render
name string Name for reports and the default output path
background boolean Return immediately; the editor keeps rendering frame by frame (default false)
movie_render {"sequence": "Intro", "output": "renders/intro.mp4", "resolution": "1080p", "fps": 24, "shutter": 0.5, "samples": 16}
skywalker call movie_render '{"sequence": "Intro", "output": "renders/intro.mp4", "resolution": "1080p", "fps": 24, "shutter": 0.5, "samples": 16}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "movie_render",
    "arguments": {
      "sequence": "Intro",
      "output": "renders/intro.mp4",
      "resolution": "1080p",
      "fps": 24,
      "shutter": 0.5,
      "samples": 16
    }
  }
}

render_layers

Render layers and cull masks mutating

Render layers (20): MeshRenderer.layers says which layers a mesh is on; Camera.cullMask and Light.cullMask say which layers a camera draws and a light illuminates (a mesh is drawn / lit when they share a layer). Name layers in game.json and use the names everywhere. action get (default): named layers, meshes per layer, cameras and lights with restricted masks, plus warnings (meshes no camera sees, lights that light nothing). action name: {layer: 2, name: "player"} (name "" clears). action set: {entities, layers} puts meshes on layers and/or {entities, cull_mask} sets cameras' and lights' masks; masks accept a number (raw bits), a layer name, "3", "all", "none" or a list; mode add/remove edits the existing mask instead of replacing it. Uses: a rim light only on the hero ({entities:["RimLight"], cull_mask:"hero"}), first-person arms hidden from a mirror camera, editor-only helpers on layer 20 hidden from the game camera. Example: {"action":"set","entities":["Hero"],"layers":["world","hero"]}

Argument Type Required Description Values
action string get (default), name a layer, or set masks get name set
layer integer name: layer number 1..20
name string name: the layer's name ("" removes it)
entities integer | string[] set: entities to change
layers any set: the meshes' layers (number, name, "all", or a list of names / layer numbers)
cull_mask any set: the cameras' / lights' cull mask (same forms as layers)
mode string set: replace the mask (default), add layers to it, or remove them replace add remove
render_layers {"action": "set", "entities": ["Hero"], "layers": ["world", "hero"]}
skywalker call render_layers '{"action": "set", "entities": ["Hero"], "layers": ["world", "hero"]}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "render_layers",
    "arguments": {
      "action": "set",
      "entities": [
        "Hero"
      ],
      "layers": [
        "world",
        "hero"
      ]
    }
  }
}

shadow_atlas_info

Inspect point/spot light shadows read-only

Which point and spot lights cast shadows this frame and how: projection (spot view, cube = 6 views, dual_paraboloid = 2 views), atlas slot and resolution, whether it was re-rendered this frame or reused from the static cache, and why a light has no shadow (disabled, out_of_view, beyond_max_distance, over_light_budget, atlas_full, pending). Also the atlas size and memory, slots used per quadrant, the per-frame budgets (Environment.localShadowLights / localShadowUpdates) and warnings. Renders one real-time frame of view first (default: the editor camera). Use it when light leaks through walls, a shadow is missing or blocky, or to check the cost of many lamps; see the atlas itself with viewport_capture debug_view "shadow_atlas". With entity, also lists the meshes that block that light (casters, first 40) and the small fixtures right at the light that are ignored (lamp heads, bulbs). invalidate=true re-renders every cached shadow on the next frame. Example: {"view": "scene"} or {"entity": "Street Lamp 3"}.

Argument Type Required Description Values
view any Camera of the frame to inspect: "editor" (default), "scene", or
entity integer | string Only report this light entity
render boolean Render a frame of view first (default true; false = the last frame rendered)
invalidate boolean Force every cached shadow to re-render (after editing meshes outside the engine, or to measure the full cost)
shadow_atlas_info {"view": "scene"}
skywalker call shadow_atlas_info '{"view": "scene"}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "shadow_atlas_info",
    "arguments": {
      "view": "scene"
    }
  }
}

light_shadows

Configure light shadows mutating

Turn shadows of point/spot lights on or off and tune them, for one light, a list, or every light ("all", optionally only one kind). Shadows stop light leaking through walls and floors; they cost an atlas slot (cube point light = 6 views, spot = 1) but are cached while nothing in range moves. Tuning: resolution (slot px hint: 256 small, 1024 hero light; 0 = automatic), mode (cube | dual_paraboloid for cheaper point lights), bias (m; raise against speckled self-shadowing, lower if shadows float), normal_bias (texels), max_distance (m from the camera beyond which the shadow fades out). One undo step. Example: {"lights": "all", "kind": "point", "enabled": true, "max_distance": 40}.

Argument Type Required Description Values
lights any yes A light entity (id or name), a list of them, or "all"
kind string With "all": only lights of this kind point spot
enabled boolean Cast shadows (Light.castShadows)
resolution integer Atlas slot hint in px (0 = automatic, 128..2048)
mode string Point light projection cube dual_paraboloid
bias number Depth bias in meters (default 0.02)
normal_bias number Normal offset in shadow-map texels (default 1)
max_distance number Camera distance (m) where the shadow fades out (0 = no limit)
light_shadows {"lights": "all", "kind": "point", "enabled": true, "max_distance": 40}
skywalker call light_shadows '{"lights": "all", "kind": "point", "enabled": true, "max_distance": 40}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "light_shadows",
    "arguments": {
      "lights": "all",
      "kind": "point",
      "enabled": true,
      "max_distance": 40
    }
  }
}

probe_add

Add a reflection probe mutating

Add a reflection probe: a captured cubemap that glossy floors, metal and glass inside its volume reflect instead of the sky (box-projected, so reflections line up with the walls), and that gives them their ambient light. Put one per room, corridor or street section, with the capture point in open space at about eye height. size "auto" (default) casts rays from position to the nearest meshes in the six axis directions and fits the box to the room (open directions get 8 m): the volume reaches blend_distance past the walls and the reflections project onto the room (projectionSize); or give [x, y, z] in meters. interior=true for closed rooms: no sky light leaks in (the probe replaces it; windows still show the sky). update: once (default, cached until it moves or probe_bake), on_change (re-captures when something in range changes), realtime (every interval frames, within Environment.probeUpdates faces per frame). The new probe is captured right away and its state returned (see probe_info). Example: {"name": "Hall Probe", "position": [0, 1.6, 0], "interior": true}.

Argument Type Required Description Values
name string Entity name (default "Reflection Probe")
position number[3] yes Capture point in meters (the room's open space, ~1.5 m up)
parent integer | string Parent entity
size any Box size [x, y, z] in meters, or "auto" (default): fit the room around position
shape string Influence volume (default box) box sphere
radius number Sphere radius in meters
interior boolean Closed room: no sky light inside (default false)
update string When to capture (default once) once on_change realtime
interval integer realtime: frames between captures (default 1)
resolution integer Face size in px: 64, 128, 256 (default), 512
priority integer Higher wins where volumes overlap (default 0)
blend_distance number Meters over which the probe fades at its edges (default 1)
box_projection boolean Parallax-correct reflections against the box (default true)
intensity number Brightness multiplier (default 1)
ambient string Diffuse ambient inside the volume (default probe) probe sky color
ambient_color string Interior / color ambient "#rrggbb"
cull_mask any Render layers the capture draws: names, numbers, "all" (default) or a list
render boolean Capture it now and return its state (default true)
probe_add {"name": "Hall Probe", "position": [0, 1.6, 0], "interior": true}
skywalker call probe_add '{"name": "Hall Probe", "position": [0, 1.6, 0], "interior": true}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "probe_add",
    "arguments": {
      "name": "Hall Probe",
      "position": [
        0,
        1.6,
        0
      ],
      "interior": true
    }
  }
}

probe_bake

Re-capture reflection probes read-only

Re-capture reflection probes now (after moving furniture, changing lights or the sky): renders one still frame of view, which captures every invalidated probe in full regardless of the per-frame face budget, and returns their state (slot, captures, GPU time) plus warnings. probes: one entity, a list or "all" (default). render=false only marks them; they re-capture over the next frames (Wander: probe_bake()). Example: {"probes": ["Hall Probe"]}.

Argument Type Required Description Values
probes any A probe entity, a list of them, or "all" (default)
view any Camera of the frame: "editor" (default), "scene", or
render boolean Render the frame that captures them now (default true)
probe_bake {"probes": ["Hall Probe"]}
skywalker call probe_bake '{"probes": ["Hall Probe"]}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "probe_bake",
    "arguments": {
      "probes": [
        "Hall Probe"
      ]
    }
  }
}

probe_info

Inspect reflection probes read-only

Reflection probes and their atlas: the budget (Environment.probeBudget probes, probeUpdates faces per frame), atlas resolution / slots / memory, and per probe: slot and debug color (the reflection_probes debug view draws it), whether it is captured, lighting this view, waiting for the face budget, over budget or out of view, captures so far, last capture GPU time, stale (something in range changed since a once probe was captured: run probe_bake), size, update mode. Warnings: overlapping volumes with equal priority, probes with no geometry in them, capture points inside a mesh, atlas exhaustion. Renders one real-time frame of view first. See the volumes with viewport_capture {debug_view: "reflection_probes"} and a probe's cubemap with viewport_capture {probe: "Hall Probe"}. Example: {"entity": "Hall Probe"}.

Argument Type Required Description Values
view any Camera of the frame: "editor" (default), "scene", or
entity integer | string Only report this probe
render boolean Render a frame of view first (default true; false = the last frame)
probe_info {"entity": "Hall Probe"}
skywalker call probe_info '{"entity": "Hall Probe"}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "probe_info",
    "arguments": {
      "entity": "Hall Probe"
    }
  }
}

particles2d_create

Create 2D particles mutating

Create pixel-art particles for a 2D game from a preset, ready to tweak: rain (streaks over the whole view), drizzle, snow (drifting flakes), leaves and petals (falling, swaying), fireflies (glowing, twinkling, wandering), smoke (chimney puffs), ripples (puddle rings while it rains), dust (motes in sunlight), sparkle (water glints). Weather presets (rain, drizzle, snow, ripples) wrap: their box tiles endlessly so they fill whatever the camera shows, wherever the entity is. Untextured particles are solid pixel rectangles (pixelSize texels); give a texture sheet (with columns/rows, frames, animate random|life|loop) for leaves, flakes or ripple rings. overrides patches fields, e.g. {"rate": 400, "color": "#9fc4ff", "pixelsPerUnit": 16}. Simulated on fixed ticks while playing (deterministic, reset on stop). Example: particles2d_create {"preset": "fireflies", "position": [12, 4, 0], "overrides": {"area": [14, 6]}}.

Argument Type Required Description Values
preset string yes Preset rain drizzle snow leaves petals fireflies smoke ripples dust sparkle
name string Entity name (default: the preset's name)
position number[3] World position (center of the emission box)
parent integer | string Parent entity (e.g. a chimney)
overrides object particles2d fields to change
particles2d_create {"preset": "fireflies", "position": [12, 4, 0], "overrides": {"area": [14, 6]}}
skywalker call particles2d_create '{"preset": "fireflies", "position": [12, 4, 0], "overrides": {"area": [14, 6]}}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "particles2d_create",
    "arguments": {
      "preset": "fireflies",
      "position": [
        12,
        4,
        0
      ],
      "overrides": {
        "area": [
          14,
          6
        ]
      }
    }
  }
}

particles2d_info

2D particle stats read-only

Live state of particles2d emitters: particles alive, cap, rate, box, wrap, and the bounds the live particles cover (world units). Use it to check that weather is running while playing, or to find emitters that hit their maxParticles cap. Without entity lists every emitter in the scene.

Argument Type Required Description Values
entity integer | string Emitter (default: all)
particles2d_info {}
skywalker call particles2d_info '{}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "particles2d_info",
    "arguments": {}
  }
}

locale_list

List locales read-only

Lists the game's locales: the current one and its fallback chain (pt-BR -> pt -> source), the source language, every locale with strings (count, coverage, files under locale/), game.json localization settings, strings requested at run time that no table has, and table file problems. Start here before translating or checking.

Takes no arguments.

locale_list {}
skywalker call locale_list '{}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "locale_list",
    "arguments": {}
  }
}

locale_set

Set the locale read-only

Switches the language the game shows: texts written as "@key" (ui.text, ui.placeholder, text.text), tr() calls and #line dialogue lines follow at once. While playing it acts like Wander's set_locale (undone when play stops); while editing it previews a language in the editor until changed. "" returns to the default (game.json localization.locale, else the source language). Then viewport_capture / ui_inspect to look for overflow. Example: {"locale": "fr"}.

Argument Type Required Description Values
locale string yes Locale code ("fr", "pt-BR", "en-XA" for the pseudo-locale); "" = default
locale_set {"locale": "fr"}
skywalker call locale_set '{"locale": "fr"}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "locale_set",
    "arguments": {
      "locale": "fr"
    }
  }
}

locale_check

Check the string tables read-only

Audits localization and returns findings an agent can fix one by one: per locale the keys with no translation (and coverage); keys the project uses ("@key" fields, tr("key") in scripts, #line dialogue ids) that no table defines; unused keys; placeholder mismatches between a translation and the source ({name} missing or extra); invalid messages; plurals missing forms the language needs (ru: one/few/many, ar: zero..many); strings longer than their budget (the table's max column, or source length x budget, default game.json maxLengthRatio); characters no font covers (CJK, Arabic...: add a font); keys requested at run time but missing. Example: {"locale": "de", "budget": 1.3}.

Argument Type Required Description Values
locale string Only this locale (default: all)
budget number Max length ratio translation / source, e.g. 1.3 (default game.json localization.maxLengthRatio)
max_findings integer Most findings listed per category (default 200)
locale_check {"locale": "de", "budget": 1.3}
skywalker call locale_check '{"locale": "de", "budget": 1.3}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "locale_check",
    "arguments": {
      "locale": "de",
      "budget": 1.3
    }
  }
}

locale_extract

Extract hard-coded texts mutating

Scans the open scene, scenes, prefabs, dialogue scripts and Wander code for hard-coded player-facing texts (ui.text, ui.placeholder, text.text, dialogue lines and choices without a #line id, .text = "..." in scripts) and proposes a key for each ("main_menu.play", "dlg.intro.start.003"); a text whose source string already exists reuses its key. With apply: true it writes the source strings to a CSV table (default locale/strings.csv), replaces the open scene's fields with "@key" (one undoable edit) and tags dialogue lines with #line:; script strings are only reported (use tr("key")). Example: {"apply": true}.

Argument Type Required Description Values
apply boolean Write the table, update the open scene and tag dialogue lines (default false: proposals only)
file string CSV table to write (default locale/strings.csv)
max integer Most proposals returned (default 300)
locale_extract {"apply": true}
skywalker call locale_extract '{"apply": true}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "locale_extract",
    "arguments": {
      "apply": true
    }
  }
}

locale_pseudo

Generate a pseudo-locale mutating

Writes a pseudo-locale for layout testing: every string accented and about 30% longer inside brackets ("Play" -> "[Ƥļáý~~]"), placeholders, plurals and rich-text tags intact, so truncation, overflow and hard-coded texts (they stay plain) jump out. Writes locale/pseudo.csv with the locale en-XA and, by default, switches to it (like locale_set). Then viewport_capture and ui_inspect each menu; locale_set {"locale": ""} goes back. Example: {"expand": 0.4}.

Argument Type Required Description Values
expand number Extra length, 0..2 (default 0.3 = 30% longer)
locale string Pseudo-locale code (default en-XA)
activate boolean Switch to it afterwards (default true)
locale_pseudo {"expand": 0.4}
skywalker call locale_pseudo '{"expand": 0.4}' --project my_game
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "locale_pseudo",
    "arguments": {
      "expand": 0.4
    }
  }
}