Create and edit materials and material instances — graph nodes, parameters, functions, custom HLSL, and instance overrides (MaterialService + MaterialNodeService).
Install the "materials" agent skill from https://github.com/kevinpbuckley/VibeUE/tree/master/Content/Skills/materials into .claude/skills/materials/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "materials", then confirm the skill loads.
Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
Type this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
skills CLI
$ npx skills add kevinpbuckley/VibeUE --skill materials -a codex
Project install goes to .agents/skills/; add -g for ~/.codex/skills/.
Install the "materials" agent skill from https://github.com/kevinpbuckley/VibeUE/tree/master/Content/Skills/materials into .agents/skills/materials/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "materials", then confirm the skill loads.
Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
skills CLI
$ npx skills add kevinpbuckley/VibeUE --skill materials -a cursor
Project install goes to .agents/skills/; add -g for ~/.cursor/skills/.
Install the "materials" agent skill from https://github.com/kevinpbuckley/VibeUE/tree/master/Content/Skills/materials into .cursor/skills/materials/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "materials", then confirm the skill loads.
Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
skills CLI
$ npx skills add kevinpbuckley/VibeUE --skill materials -a gemini-cli
Project install goes to .agents/skills/; add -g for ~/.gemini/skills/.
Install the "materials" agent skill from https://github.com/kevinpbuckley/VibeUE/tree/master/Content/Skills/materials into .gemini/skills/materials/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "materials", then confirm the skill loads.
Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
GitHub CLI
$ gh skill install kevinpbuckley/VibeUE materials
Installs for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
skills CLI
$ npx skills add kevinpbuckley/VibeUE --skill materials -a github-copilot
Project install goes to .agents/skills/; add -g for ~/.copilot/skills/.
Install the "materials" agent skill from https://github.com/kevinpbuckley/VibeUE/tree/master/Content/Skills/materials into .github/skills/materials/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "materials", then confirm the skill loads.
GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
skills CLI
$ npx skills add kevinpbuckley/VibeUE --skill materials -a opencode
OpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
Install the "materials" agent skill from https://github.com/kevinpbuckley/VibeUE/tree/master/Content/Skills/materials into .opencode/skills/materials/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "materials", then confirm the skill loads.
OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
Facts
Skill name
materials
GitHub stars
726
Token cost
~5.3k tokens
SKILL.md length
1,572 words
Files
8 (incl. scripts, references)
Skills in repo
39
Repo updated
First seen
Licence
MIT
At a glance
Create and edit materials and material instances — graph nodes, parameters, functions, custom HLSL, and instance overrides (MaterialService + MaterialNodeService).
Works in 6 steps: Export the graph JSON and review… → Plan your changes — identify which nodes… → Disconnect existing connections if… → …
The user asks to create
SKILL.md covers Critical Rules, Task Index, Sub-docs and Verification, plus 2 more sections
Edit a material/material instance
What it does
Materials is an agent skill from kevinpbuckley/VibeUE. Create and edit materials and material instances — graph nodes, parameters, functions, custom HLSL, and instance overrides (MaterialService + MaterialNodeService). Use when the user asks to create or edit a material/material instance, wire material nodes, add material parameters, set blend/shading modes, or recreate a material graph. For landscape materials load landscape-materials. Also use for generated organic PBR detail, texture seams, projection coordinates, and surface-normal diagnosis.
Its SKILL.md is about 5.3k tokens, which your agent loads only when the skill is triggered. The skill folder holds 9 other files, including scripts and reference files (for example `reference.md`, `references/generated-organic-surfaces.md` and `workflows.md`).
It sits in Game Development, covering Shaders. The repository describes itself as: Unreal Engine Vibe Coding tool. The licence is MIT.
When your agent uses it
The user asks to create
Edit a material/material instance
Wire material nodes
Add material parameters
Example prompts
“/materials”
Requirements
Python 3
Workflow steps
6 steps, taken from the first numbered list in SKILL.md.
1Export the graph JSON and review expressions + connections
2Plan your changes — identify which nodes to keep, replace, or add
3Disconnect existing connections if needed before reconnecting
4Add only the new nodes that don't already exist
5Reconnect outputs to their correct sources
6Compile and save
What it can do on your machine
Read from SKILL.md and the folder at commit dc051be. It shows what the files ask for, not the result of running them.
Tool permissions
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Runs code
Ships 4 files in scripts/, which the agent can run.
From the folder's file list and the shell code blocks in SKILL.md.
Network
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Credentials
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Context cost
Materials loads about 5.3k tokens when it runs, and up to ~6k if it reads all its reference files. Until then it costs about 127 tokens; SKILL.md has 1,572 words of instructions outside code blocks.
Always· name and description, kept in context so the agent knows when to use it
~127
When it runs· the whole SKILL.md, loaded when a task matches
~5.3k
With references· SKILL.md plus every file in references/, read only if the agent opens them
~6k
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
Safety
Auto-check passed
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); the scripts in this folder are not scanned.
Download SKILL.mdSave it as .claude/skills/materials/SKILL.md (or your agent's skills folder). This skill also uses 7 other files; get the full folder from GitHub.
name
materials
description
Create and edit materials and material instances — graph nodes, parameters, functions, custom HLSL, and instance overrides (MaterialService + MaterialNodeService). Use when the user asks to create or edit a material/material instance, wire material nodes, add material parameters, set blend/shading modes, or recreate a material graph. For landscape materials load landscape-materials. Also use for generated organic PBR detail, texture seams, projection coordinates, and surface-normal diagnosis.
🧠 Brains complement: IF an unreal-engine-skills-manager tool (external MCP) exists in this session, call it with {action: "load", skill: "materials-and-shaders"} for UE domain knowledge on this topic — correct APIs, architecture, best practices — and treat it as the rubric for any review / "best practices" question. If no such tool is available (e.g. running under Claude Code or Codex without that MCP), skip this line entirely and proceed with this skill alone — do NOT attempt the call.
Material System Skill
For generated textures on large organic surfaces, seams after triplanar projection,
or overly strong tissue relief, read generated organic surfaces.
For gray/default surfaces after mesh reimport, use the asset-management skill's
LOD-section mapping helper before changing the shader or buying new textures.
Critical Rules
🚨 Connecting Function Call Inputs: Use Bare Names, Not Display Names
get_expression_pins(mat, expr_id) (MaterialNodeService) returns input pins with display labels including type suffixes like "TextureObject (T2d)", "TextureSize (V3)", "Use High Quality Normals (SB)". Connections are made through the engineMaterialTools.connect_expressions call, and UE's connect API matches against the bare input name only — never pass these display labels as to_input_name:
python
MT = "editor_toolset.toolsets.material.MaterialTools"
# ❌ WRONG — display label with type suffix; connection fails silently or gets ignored
call_tool(tool_name="connect_expressions", toolset_name=MT,
arguments={"from_expression": src_id, "from_output_name": "",
"to_expression": fn_id, "to_input_name": "TextureObject (T2d)"})
# ✅ CORRECT — bare name
call_tool(tool_name="connect_expressions", toolset_name=MT,
arguments={"from_expression": src_id, "from_output_name": "",
"to_expression": fn_id, "to_input_name": "TextureObject"})
The authoritative source for input names is get_function_info(function_path).inputs — those are the bare names (TextureObject, TextureSize, WorldPosition, etc.). For built-in expressions, use get_inputs_for_material_expression from unreal.MaterialEditingLibrary.
⚠️ Why this is easy to miss: get_expression_pins returns is_connected=True for both real and phantom connections, and our service used to silently produce phantom wires when given display names. The wires would appear in the graph export but the shader compiler would never see the texture flow through. Always verify with unreal.MaterialEditingLibrary.get_used_textures(mat) after wiring — if it returns 0 on a material that samples textures, your connections are phantom.
🚨 Verifying Wiring: Use get_material_diagnostics
MaterialNodeService.get_material_diagnostics(path) is the canonical way to verify a material is sampling textures and compiling cleanly. It returns the real compile errors, the textures the shader actually references, and node-type counts. Always run it after non-trivial graph rewiring:
python
diag = unreal.MaterialNodeService.get_material_diagnostics("/Game/Materials/M_Foo")
assert diag.success
print(f"compiled ok: {diag.is_compiled_ok}")
print(f"compile errors ({len(diag.compile_errors)}):")
for e in diag.compile_errors: print(f" {e}")
print(f"referenced textures ({len(diag.referenced_texture_paths)}):")
for t in diag.referenced_texture_paths: print(f" {t}")
print(f"expression count: {diag.expression_count}")
print(f"texture sample count: {diag.texture_sample_count}")
print(f"texture object parameter count: {diag.texture_object_parameter_count}")
print(f"function call count: {diag.function_call_count}")
Sample compile errors caught by this:
"(Function WorldAlignedTexture) (Node TextureSample) Sampler type is Color, should be Masks for /Game/.../T_xevtfjz_2K_ORM" — ORM/data textures need SAMPLERTYPE_Masks or SAMPLERTYPE_LinearColor, not the default SAMPLERTYPE_Color.
Type mismatch errors when wiring scalars to vector inputs without proper conversion.
Missing required inputs on function calls.
Why prefer this over the older signals:
unreal.MaterialEditingLibrary.get_used_textures(mat) is unreliable for multi-branch graphs (BC + Normal + ORM, or anything with ComponentMask after a function-call output). It returns 0 even when the material is sampling textures correctly. Don't use it as proof of broken wiring.
Visual confirmation via the material editor preview is also useful but harder. Asset-editor
capture now goes through the engineEditorAppToolset (toolset_name
EditorToolset.EditorAppToolset via call_tool) — it opens, focuses, and captures the asset
editor. (The old unreal.ScreenshotService and its capture_asset_editor are gone.) After the
capture, attach_image(...) the returned file to inspect it.
⚠️ Best-effort focus: when many asset editors are already open as tabs, the editor tab may not switch reliably. Close other asset editors first if the screenshot keeps catching the wrong tab:
python
ed = unreal.get_editor_subsystem(unreal.AssetEditorSubsystem)
for path in [other_open_paths]:
ed.close_all_editors_for_asset(unreal.load_asset(path))
🚨 Tiling on Basic Shapes: Prefer a Child MI with Tiling Override
For Megascans M_MS_Srf and similar surface materials with a Tiling scalar parameter, the cleanest per-actor tiling fix is a child material instance with Tiling overridden — not a UV transform on the mesh. UV transforms apply to all faces uniformly and break orientation; the parent material's UV mapping is already correct per face.
python
# ✅ CORRECT pattern for "this disc shows brick too sparse / too dense"
# Material instances are created and overridden through the engine MaterialInstanceTools toolset.
MIT = "editor_toolset.toolsets.material_instance.MaterialInstanceTools"
call_tool(tool_name="create_material_instance", toolset_name=MIT,
arguments={"parent_material": "/Game/Fab/Megascans/.../MI_xevtfjz",
"asset_name": "MI_xevtfjz_Disc", "folder_path": "/Game/Materials/"})
ip = "/Game/Materials/MI_xevtfjz_Disc"
call_tool(tool_name="set_scalar_parameter", toolset_name=MIT,
arguments={"instance_path": ip, "parameter_name": "Tiling", "value": 4.0})
unreal.EditorAssetLibrary.save_asset(ip)
# assign ip to the actor
For triplanar / world-aligned (where the same material works on cube, sphere, cylinder with no UV concerns), build a master material wrapping WorldAlignedTexture / WorldAlignedNormal (functions live under /Engine/Functions/Engine_MaterialFunctions01/Texturing/). Use TextureObjectParameter (not TextureSampleParameter2D) to feed the TextureObject input on those functions — the function brings its own sampler. Feed TextureSize from a Constant3Vector or Multiply(Scalar, Constant3Vector(1,1,1)); a bare scalar will broadcast incorrectly to V3 in this input slot.
🚨 Inspect Before Modifying Existing Materials
Before adding, removing, or reconnecting nodes in an existing material, you MUST export and review the current graph first:
python
import unreal, json
path = "/Game/Materials/M_Existing"
# Step 1: Get high-level info (blend mode, shading model)
info = unreal.MaterialService.get_material_info(path)
print(f"Blend: {info.blend_mode}, Shading: {info.shading_model}")
# Step 2: Export the full node graph
graph = json.loads(unreal.MaterialNodeService.export_material_graph(path))
print(f"Expressions: {len(graph['expressions'])}")
print(f"Connections: {len(graph['connections'])}")
print(f"Output connections: {len(graph['output_connections'])}")
# Step 3: Review what already exists
for expr in graph['expressions']:
name = expr.get('parameter_name') or expr.get('class')
print(f" [{expr['id']}] {expr['class']} - {name}")
for oc in graph['output_connections']:
print(f" Output '{oc['property']}' ← expr {oc['expression_id']}")
Why this matters:
The material may already have nodes connected to the output you want to modify
Blindly adding nodes creates duplicates and orphaned connections
You need to know what IDs exist so you can reconnect or replace, not just append
export_material_graph returns the ground truth — use it to plan your edits
Workflow for modifying existing materials:
Export the graph JSON and review expressions + connections
Plan your changes — identify which nodes to keep, replace, or add
Disconnect existing connections if needed before reconnecting
Add only the new nodes that don't already exist
Reconnect outputs to their correct sources
Compile and save
🚨 Return Types — create_parameter/create_function_call Return .id, NOT the Raw Object
Material/instance creation now goes through the engine toolsets (MaterialTools.create_material
returns the Material object itself — not a result-with-.asset_path; MaterialInstanceTools
creates the instance), so build the asset path yourself from folder_path + asset_name:
python
# Engine: create_material returns the Material object (no .asset_path) — reference by path you chose.
call_tool(tool_name="create_material",
toolset_name="editor_toolset.toolsets.material.MaterialTools",
arguments={"folder_path": "/Game/Materials/", "asset_name": "M_MyMat"})
path = "/Game/Materials/M_MyMat"
# Engine: create the instance via MaterialInstanceTools, then reference the path you chose.
call_tool(tool_name="create_material_instance",
toolset_name="editor_toolset.toolsets.material_instance.MaterialInstanceTools",
arguments={"parent_material": "/Game/Materials/M_Base",
"asset_name": "MI_Red", "folder_path": "/Game/Materials/"})
instance_path = "/Game/Materials/MI_Red"
The surviving VibeUE graph-authoring calls still return result objects — extract the field:
MaterialNodeService - Build material graphs, expressions, parameters
⚠️ Compile After Graph Changes
Full graph workflow: create nodes → connect nodes → connect to material output → compile → save.
python
mns = unreal.MaterialNodeService
expr = mns.create_parameter(path, "Vector", "BaseColor", ...)
# Wire an expression output straight into a material output (BaseColor, Normal, Roughness, ...).
# output_name="" selects output 0; property accepts "BaseColor" or "MP_BaseColor" (case-insensitive).
mns.connect_expression_to_output(path, expr.id, "", "BaseColor") # returns True only after a read-back
# ...and to clear it again: mns.disconnect_output(path, "BaseColor")
unreal.MaterialService.compile_material(path) # REQUIRED after graph changes
unreal.EditorAssetLibrary.save_asset(path)
⚠️ expr.id is session-specific ("<Class>_<pointer>") — it changes on editor restart. For a
durable reference use expr.object_path, which every id-based call (connect_expression_to_output,
get_expression_details, set_expression_property, ...) also accepts. Empty / bogus / foreign ids
are now rejected (they used to silently resolve to expression index 0).
The engine's own MaterialTools.connect_to_output (call_tool, material_property="MP_BaseColor") still
works and is interchangeable — use whichever fits the surrounding code.
⚠️ Parameter Types
Type
Use
Example Value
Scalar
Single float
0.5
Vector
Color/vector
(R=1.0,G=0.0,B=0.0,A=1.0)
Texture
Texture parameter
/Game/T_Diffuse.T_Diffuse
TextureObject
Texture object (no sampler)
/Game/T_Diffuse.T_Diffuse
StaticSwitch
Static bool switch
true or false
⚠️ Material Output Names
BaseColor, Metallic, Specular, Roughness
EmissiveColor, Normal, Opacity, OpacityMask
WorldPositionOffset, AmbientOcclusion
Show full SKILL.md (626 more words)Show less
⚠️ Discovering Node Types & Categories
To answer "what nodes / categories are available", use the MaterialNodeService discovery
calls — do not hunt through discover_python_module or a material-editor palette API
(there isn't a Python one):
To create a node, pass the bare class name minus the MaterialExpression prefix
("Add", "Multiply", "Constant3Vector", "OneMinus", "LinearInterpolate"/"Lerp") as
expression_class to the engine MaterialTools.add_expression call (single node), or to the
surviving MaterialNodeService.batch_create_expressions (many nodes in one transaction).
⚠️ Check Node Existence
python
# WRONG - crashes if not found
add_node = next((n for n in nodes if "Add" in n.display_name))
# CORRECT
add_node = next((n for n in nodes if "Add" in n.display_name), None)
if add_node:
pins = unreal.MaterialNodeService.get_expression_pins(mat_path, add_node.id)
⚠️ Property Names
Use discover_python_class() first (batch: class_name='unreal.A, unreal.B'):
MaterialExpressionTypeInfo uses display_name, NOT name
MaterialOutputConnectionInfo uses connected_expression_id, NOT expression_id
id, class_name, display_name, category, description, pos_x, pos_y, is_parameter, parameter_name, input_names, output_names — the class is class_name, not expression_class
property_name, display_name, property_type, current_value, allowed_values, category, is_editable, is_advanced — it's property_name/current_value, not name/value (differs from MaterialNodePropertyInfo above)
🚨 Setting Material Properties: set_property Takes Display OR Internal Name
MaterialService.set_property(path, name, value) / get_property accept either the editor
display name ("Two Sided", "Blend Mode", "Opacity Mask Clip Value") or the internal
property name ("TwoSided", "BlendMode", "OpacityMaskClipValue") — matching is
case-insensitive and ignores spaces. Both of these work:
python
unreal.MaterialService.set_property(path, "Two Sided", "true") # display name
unreal.MaterialService.set_property(path, "BlendMode", "BLEND_Masked") # internal name
unreal.MaterialService.set_property(path, "OpacityMaskClipValue", "0.33")
unreal.MaterialService.compile_material(path) # enum/blend changes need a recompile
unreal.MaterialService.save_material(path)
⚠️ set_property returns a bool (True/False), it does not raise on an unknown
property — a False means the name didn't resolve or the value didn't parse. Don't assume
success: check the return, or verify with get_property / summarize afterward. Enum values
are the BLEND_* / MSM_* / MD_* identifiers (see below), not the friendly labels.
Discover legal enum values, don't guess:list_properties / get_property_info populate
allowed_values for enum properties — including the classic TEnumAsByte ones
(BlendMode, ShadingModel, MaterialDomain). Read them instead of probing unreal for the
enum class:
python
for p in unreal.MaterialService.list_properties(path):
if p.property_name == "BlendMode":
print(p.allowed_values)
# ['BLEND_Opaque','BLEND_Masked','BLEND_Translucent','BLEND_Additive', ...]
Common material property internal names: TwoSided, BlendMode, ShadingModel,
MaterialDomain, OpacityMaskClipValue, bUsedWithStaticLighting, DitheredLODTransition.
Task Index
Task
Workflow
Sample script
Create a material (+ params)
workflows.md → Create a basic material
scripts/create_material.txt
Create a material instance
workflows.md → Create a material instance
scripts/create_instance.txt
Add texture / math / function / HLSL nodes
workflows.md
—
Build a graph with batch ops
workflows.md → Batch create/connect/set
scripts/material_graph_batch.txt
Inspect/export an existing graph
reference.md → Export JSON schema
scripts/export_graph.txt
Recreate a material from export
reference.md → Recreate from export
—
Verify wiring / compile
Critical Rules → get_material_diagnostics
—
Sub-docs
workflows.md — create material/instance, texture/math/function/HLSL/collection/switch nodes,
set properties, and batch create/connect/set.
reference.md — parameter types, output names, enum formats, the export_material_graph JSON
schema, recreate-from-export steps, and Material Function authoring.
Verification
After non-trivial wiring run MaterialNodeService.get_material_diagnostics(path) and confirm
is_compiled_ok and the expected referenced_texture_paths — do not rely on get_used_textures
(unreliable for multi-branch graphs). compile_material + save_asset to persist.
Related skills
landscape-materials — LandscapeLayerBlend nodes.
landscape-auto-material — production landscape materials (functions, RVT, instances).
Additional gotchas
StaticMesh.get_editor_property("static_materials") returns struct COPIES, so writing the array back is a silent no-op; use set_material(index, mat) and confirm with get_material(index).
MEL.set_material_instance_scalar_parameter_value returns False on success, and the static-switch setter returns False while the write still lands — verify by readback; MI reads return 0.0 and writes no-op while PIE runs.
Changing body_setup.collision_trace_flag does not rebuild the physics mesh; toggle StaticMeshEditorSubsystem.enable_section_collision off then on.
Enumerate expressions with ObjectIterator(unreal.MaterialExpression) filtered on path.startswith(mat_path + ":") (class-specific iterators miss nodes); delete_material_expression enables in-place rebuilds that keep instance overrides when parameter names match.
Pin names: StaticSwitchParameter inputs are True/False; LandscapeLayerBlend inputs are "Layer <name>"/"Height <name>"; LayerBlendInput fields are invisible to dir() but set_editor_property("layer_name"|"blend_type"|"preview_weight") works.
Gate optional master-material features behind STATIC switches (a scalar "off" still samples); measure with MEL.get_statistics. A material used on a Niagara sprite renderer needs bUsedWithNiagaraSprites.
Delete a material with AssetDiscoveryService.delete_asset_unattended; it refuses (and returns a result struct naming the holder) when the material is still referenced or held by a Python global, instead of wedging on a modal dialog.
Materials next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
Walks an Unreal Engine MCP agent through building materials, Niagara particle systems, Chaos destruction, and curve assets with inspect-then-edit steps.
A skill your agent uses when working with RenderDoc capture files (.rdc), analyzing GPU frames, tracing shaders, inspecting draw calls, or running CI assertions against GPU captures.
Previews, validates and bakes bone-rotation edits on Unreal Engine AnimSequences, enforcing a coordinate space and a constraint-checked preview-validate-bake loop.
Creates and edits AnimSequence keyframes and bone tracks in Unreal Engine through AnimSequenceService, following an inspect, preview, validate and bake workflow with correct bone-space handling.
Imports files into Unreal Engine, exports textures, reads the Content Browser selection and checks open assets via VibeUE, plus mesh reimport material fixes.
Changes Unreal Engine console variables, scalability levels and raw INI values through VibeUE's Python API, now that registered settings moved to the engine's own toolset.
Create and edit materials and material instances — graph nodes, parameters, functions, custom HLSL, and instance overrides (MaterialService + MaterialNodeService). Materials is an agent skill from kevinpbuckley/VibeUE. Create and edit materials and material instances — graph nodes, parameters, functions, custom HLSL, and instance overrides (MaterialService + MaterialNodeService).
When should I use Materials?
Materials fits situations like: the user asks to create; edit a material/material instance; wire material nodes; add material parameters.
How do I install Materials in Claude Code?
Run `npx skills add kevinpbuckley/VibeUE --skill materials -a claude-code`. Or copy the skill folder (Content/Skills/materials in kevinpbuckley/VibeUE) into .claude/skills/materials in your project. Claude Code loads it when a task matches its description.
How do I install Materials in Codex?
Run `npx skills add kevinpbuckley/VibeUE --skill materials -a codex`. Or copy the skill folder (Content/Skills/materials in kevinpbuckley/VibeUE) into .agents/skills/materials in your project. Codex loads it when a task matches its description.
Can I use Materials in Cursor, Gemini CLI or GitHub Copilot?
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add kevinpbuckley/VibeUE --skill materials -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/materials, .gemini/skills/materials, .github/skills/materials and .opencode/skills/materials in your project.
What does Materials need to run?
SKILL.md names no scripts, command-line tools or credentials: Materials is instructions for the agent only. Our summary lists: Python 3.
Does Materials access the network?
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Is Materials safe to install?
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. The check reads SKILL.md only: the scripts in the folder are not scanned, so read them before running anything.
What licence does Materials use?
Materials is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
How many tokens does Materials use?
About 5.3k tokens (SKILL.md is roughly 21k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 645 tokens, read only when the agent opens those files.
What are the alternatives to Materials?
Skills that share tags, products or a category with Materials: Pixijs (RSamaium/CanvasEngine, 401 stars), New Experiment (Tresjs/tres, 3.8k stars), Assets Shader List All (IvanMurzak/Unity-MCP, 4.4k stars) and Handle Sdr Tonemap Lut (clshortfuse/renodx, 4.5k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
Who maintains Materials?
kevinpbuckley (a GitHub user) maintains it in kevinpbuckley/VibeUE, which has 726 GitHub stars. The repository holds 39 skills in this directory. The repository was last updated on October 7, 2026.
Source: kevinpbuckley/VibeUE on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.