Visual Effect Graph

SkillDev tools

Author and inspect Unity Visual Effect Graph (vfx) assets with unity-cli. Use when the user wants to read, build, or modify a .vfx graph and its systems, contexts, blocks, operators, or particle behavior, or to discover available blocks. Do not use for generic asset, material, or import operations; use `unity-asset-management` instead.

Instructions available. Your AI can read the instructions. Execution depends on the setup they require.

Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.

Then ask your AI: use the Visual Effect Graph skill

What this skill tells your AI

The instructions your AI receives, as published by akiojin/unity-cli in .claude-plugin/plugins/unity-cli/skills/unity-vfx-graph/SKILL.md and read by ahel’s review.

With --output json, results use {success, command, data, errors, warnings}. Check the exit status and envelope success first; tool-result fields in this skill are relative to data. Read failure codes from errors[0].code; see unity-cli-usage for exit-code recovery.

Author and inspect .vfx Visual Effect Graph assets: read a graph's contexts, blocks, operators and parameters, discover the node library, and apply authoring mutations. The VFX authoring API is internal to Unity, so these operations run through dedicated bridge tools (vfx_*) rather than direct component edits. This skill is the VFX complement to unity-asset-management, which handles generic asset, material, and import operations.

Use When

  • The user wants to inspect a .vfx graph's structure (contexts, blocks, operators, parameters, links, layout).
  • The user wants to discover which blocks, operators, contexts, or templates are available.
  • The user wants to build or modify a graph: add/remove/link nodes, set values and settings, manage the blackboard, systems, events, subgraphs, sticky notes, groups, or canvas layout.
  • The user wants to verify a graph compiles, or find out why it does not.
  • The user wants to drive an exposed parameter on a live VisualEffect (vfx_runtime), read/write VFX project settings or editor preferences (vfx_settings), or bake a Mesh into an SDF Texture3D (vfx_bake_sdf).

Do Not Use When

  • The task is generic asset, material, or import work; use unity-asset-management.
  • The request is about editing arbitrary serialized fields on a scene component; use unity-gameobject-edit. (vfx_runtime is only for VisualEffect public-API calls like SetFloat/SendEvent.)
  • The work is play-mode lifecycle control or input simulation; use unity-playmode-testing.

Preferred Flow

Prerequisite: the target project must have com.unity.visualeffectgraph installed. The Bridge keeps it optional; VFX_PACKAGE_MISSING means to report the missing package and obtain the user's intended package setup before authoring a graph. Input errors return {error, code} without Console Error logs; inspect the response before continuing. Runtime playback uses send_event with OnPlay.

  1. Baseline. vfx_describe_graph before mutating. Note the three oracles it carries: errors (validation + compile errors, on by default), compile (outcome of the last recompile), and layout (overlapCount + overlapping node pairs). Write down the baseline overlapCount: a hand-made graph often has a non-zero one (node sizes are estimated generously), and your job is to not add to it.
  2. Discover names. vfx_list_library with kind (block default, operator, context, parameter, template) and a filter whenever you are not certain of an exact descriptor name — the leading | and |_ separators in names like |Set|_Color are load-bearing.
  3. Mutate narrowly with vfx_apply, one op at a time. Every op response carries compile.success; read it. If it is false, the graph no longer compiles — compile.exception / compile.errors / compile.logs say why. Fix that before doing anything else; never stack more ops on a broken graph. An op that returns error was not applied at all.
  4. Place what you added — mandatory; move nothing else. Add ops only keep a new node off existing ones. Before you report, run vfx_apply op:"auto_layout". Its default scope, nodes, places only the nodes created this session — operators, and a parameter canvas node for every link made from a parameter — in free space directly left of what they feed, aligned with the consuming block's row and stacked per consumer, and moves nothing that existed before. (One exception, reported as shiftedDown: a context that grew over the next context of its own system pushes that lower part of the system down, vertically only, as a person would.) A person's canvas is theirs: re-laying out a whole system for one added block is never the right answer. Pass scope:"touched" (re-lay out every system edited this session), scope:"all", or contexts:[…] only when the user asks for a tidy-up.
  5. Box the feature with its note. A person keeps the nodes of one feature in a group box with a sticky note explaining it; do the same for what you added: group_nodes with your new nodes and note:{title, contents} creates the box and the note inside it (left of the members). Read nonMembersInside in the response — if the box swallows nodes that are not yours, move your own nodes closer together (move_node; nodes created this session move freely) and run group_nodes again, or drop the box and keep just the note. A block cannot be grouped; name it in the note.
  6. Verify and only then report. Re-run vfx_describe_graph and confirm all three: compile.success == true, no errors[] entry with type: "Error", and layout.overlapCount no higher than your baseline with no listed overlap involving a node you created (on a graph you built from scratch that means 0). Quote those three facts in your completion report. (get_compilation_state is C# script compilation and says nothing about a VFX graph; use compile / describe instead.)

Invocation: every tool runs as unity-cli raw <tool> --json '<json>'. Add --output json for a structured (parseable) result, and --port <N> to target a specific bridge (default 6400).

unity-cli raw vfx_describe_graph --json '{"assetPath":"Assets/FX/Burst.vfx"}'
unity-cli raw vfx_list_library --json '{"kind":"block","filter":"turbulence"}'
unity-cli raw vfx_apply --json '{"op":"add_block","assetPath":"Assets/FX/Burst.vfx","contextType":"Update","blockName":"Turbulence","settings":{"NoiseType":"Perlin"}}'
unity-cli raw vfx_apply --json '{"op":"add_operator","assetPath":"Assets/FX/Burst.vfx","operatorName":"Multiply"}'
unity-cli raw vfx_apply --json '{"op":"link_slots","assetPath":"Assets/FX/Burst.vfx","from":{"node":"operator","operatorIndex":0,"slot":0},"to":{"node":"block","contextType":"Update","blockIndex":0,"slot":0}}'
unity-cli raw vfx_apply --json '{"op":"add_parameter","assetPath":"Assets/FX/Burst.vfx","parameterName":"Rate","type":"Float","value":42.5,"min":0,"max":100}'
unity-cli raw vfx_describe_graph --json '{"assetPath":"Assets/FX/Burst.vfx","include":["errors"],"includeSlots":false}'
unity-cli raw vfx_apply --json '{"op":"add_block","assetPath":"Assets/FX/Burst.vfx","contextType":"Update","blockName":"Turbulence","autoCompile":false}'
unity-cli raw vfx_apply --json '{"op":"compile","assetPath":"Assets/FX/Burst.vfx"}'
unity-cli raw vfx_apply --json '{"op":"auto_layout","assetPath":"Assets/FX/Burst.vfx"}'
unity-cli raw vfx_apply --json '{"op":"group_nodes","assetPath":"Assets/FX/Burst.vfx","title":"Liquid turbulence","nodes":[{"node":"parameter","parameterIndex":12},{"node":"operator","operatorIndex":3}],"note":{"title":"Liquid turbulence","contents":"Relative-mode Perlin turbulence: Drag pulls velocity toward the noise field."}}'

Hard Rules

  • Describe narrowly on a big graph. An unfiltered vfx_describe_graph on a real graph is huge — a working hand-made graph returns ~450 KB (~112K tokens), which will swamp your context. Pass includeSlots:false to drop the slot trees that dominate it (-61%), and include:[...] to keep only the top-level sections you need (include:["errors"] + includeSlots:false is ~1K tokens). Counts and assetPath always survive, and slotsOmitted:true marks a filtered read so empty slot arrays are never mistaken for "no slots". Use the full describe only when you actually need slot values.
  • Batch edits with autoCompile:false. Every op recompiles by default, and the cost grows with the graph — 20 add_block ops cost ~34s eagerly and ~8s deferred (4.4x). Pass autoCompile:false on each op in a run of edits, then one vfx_apply op:"compile" to flush (it reports flushedDeferred). Deferred edits live in memory until that compile — a domain reload (script recompile, play-mode enter) before it discards them, so keep a deferred run short and always close it with compile. A deferred op response carries compile.deferred:true instead of a compile summary, so rule 3 above (read compile.success) applies to the flushing compile call instead.
  • Custom HLSL operator: at most 4 inputs. A VFX expression takes at most 4 parents, so a Custom HLSL operator whose function has 5+ parameters makes the asset stop compiling. set_operator_setting/add_operator refuse such a function with a clear error; pack inputs into float2/3/4 or split the function. Custom HLSL blocks have no such limit.
  • Addressing. Block ops take contextType (first context of that type) or contextIndex (absolute index from describe). Context ops (set_context_setting, remove_context, delete_system, set_system_name, set_bounds, link_flow/unlink_flow endpoints) take contextType or index — contextIndex is accepted as an alias everywhere. Prefer the index whenever a graph has two contexts of the same type.
  • New nodes take open space. Add/duplicate/insert ops and add_sticky_note refuse to drop a node on an existing one: if the spot is taken (auto or explicit position) the node is moved to the nearest free space and the response says positionAdjusted: true. Read the returned position rather than assuming the one you asked for.
  • Existing nodes are the person's. The default auto_layout (scope:"nodes") never moves a node that existed before your session. The whole-system passes (touched/all/contexts) keep every existing context's x and only re-stack vertically; move_node on such a context keeps x and says so in note. Only a system you created this session is placed in a fresh column.
  • Short edges over shared nodes. Do not route one parameter node or operator across the canvas to many consumers. Give far-apart consumers their own parameter canvas node and put cheap operators beside the system they feed. The nodes pass gives every parameter link you made a node within reach of its consumer; the whole-system passes also duplicate shared operators (duplicateShared, splitParameters), so operator indices can change after them — re-describe before addressing by index.
  • Linked slots ignore constants. set_slot_value on a linked input warns (warning) — unlink_slots first if you meant the constant.
  • Parameters must be used to survive. An exposed parameter that feeds nothing is stripped at compile and invisible at runtime (vfx_runtime hasFloat:false).
  • |Set|_X / Get|_X descriptors compose attribute blocks/operators; custom attributes are declared with add_custom_attribute and then targeted with set_block_setting setting:"attribute".

Op Index

AreaOps (all vfx_apply)Reference
Blocksadd_block, set_block_setting, set_block_enabled, reorder_block, move_block, duplicate_block, remove_blockops-graph.md
Contexts / systemsadd_context, set_context_setting, remove_context, delete_system, set_system_name, link_flow, unlink_flow, set_boundsops-graph.md, systems-and-events.md
Operatorsadd_operator, set_operator_setting, duplicate_operator, remove_operator, add_operator_input, remove_operator_input, set_operator_operand_type, rename_operator_input, reorder_operator_inputops-graph.md
Slots / linkslink_slots, unlink_slots, set_slot_value, set_slot_space, convert_to_property, convert_to_inlineops-graph.md
Blackboardadd_parameter, set_parameter, rename_parameter, set_parameter_category, rename_category, reorder_category, reorder_parameter, duplicate_parameter, remove_parameter, add_custom_attributeops-graph.md
Compile / verifycompile; describe errors / compileops-graph.md
Layout / canvasauto_layout (nodes default / touched / all / contexts), move_node, group_nodes (+ note, sticky-note members, nonMembersInside), remove_group, add_sticky_note, update_sticky_note, remove_sticky_note, reorder_sticky_note; describe layoutlayout.md
Custom HLSLadd_block "Custom HLSL", add_operator "Custom HLSL", m_HLSLCode / m_ShaderFile / function selectorcustom-hlsl.md
Events, subgraphs, templates, assetGPU/output events, create_subgraph_asset, create_from_template, insert_template, designate_template, set_instancing, set_initial_event_namesystems-and-events.md
Runtime, settings, SDFvfx_runtime, vfx_settings, vfx_bake_sdfruntime-settings-sdf.md

Examples

  • "List the contexts and blocks in Assets/FX/Burst.vfx."
  • "Add a Turbulence block to the Update context, set NoiseType to Perlin, and confirm it compiles."
  • "Create an exposed float Rate, drive the Constant Spawn Rate block with it, then tidy the canvas."
  • "Build a second particle system Init→Update→Output in this graph and confirm it is disjoint from the first."
  • "Add a Custom HLSL operator that combines three floats and a float3 into a float3."
  • "Why does this graph not compile?"
  • "Put the graph on a VisualEffect, set Rate to 7.5 at runtime, and read it back."
  • "Set the fixed time step to 0.02 in the VFX project settings."

References

  • ops-graph.md: blocks, contexts, operators, slots/links, blackboard, compile/verify — parameters and semantics for every op.
  • layout.md: the readable-canvas rules, auto_layout, move_node, groups, sticky notes, and the layout oracle.
  • custom-hlsl.md: Custom HLSL blocks and operators, the 4-input operator limit, external files, function selectors, buffer/texture types.
  • systems-and-events.md: building systems and variants, events (GPU/spawn/output), subgraphs, templates, instancing and initial event.
  • runtime-settings-sdf.md: vfx_runtime, vfx_settings, vfx_bake_sdf.
  • runtime-checklist.md: connection and instance prerequisites, runtime verification caveats.

Signals

GitHub stars
106
Forks
12
Last commit
Oct 2026
Advanced
Item type
skill
Key
unity-vfx-graph-akiojin
Source
github.com/akiojin/unity-cli