metasounds
Create and modify MetaSound Source assets — add/connect nodes, wire pins, set input defaults, and play procedurally (MetaSoundService). Use when the user asks to create a MetaSound, build or edit a MetaSound graph, add operator/input/output nodes, or generate procedural audio.
pinned to #87ec7e6updated 2 weeks ago
Ask your AI client: “install skills/metasounds”.
Requires the metahub MCP server installed in your client. Set up MCP.
mh install skills/metasoundsmetahub onboarded this repo on the author's behalf.
If you own github.com/kevinpbuckley/VibeUE on GitHub, claim the listing to take over publishing. Your claim preserves the existing eval history and badges; only the curator label is replaced with verified-publisher on your next publish.
Stars
472
Last commit
2 weeks ago
Latest release
published
- #ai
- #ai-tools
- #automation
- #blueprint
- #claude
- #copilot
- #cursor
- #game-development
- #gamedev
- #landscape
- #mcp
- #mcp-server
- #model-context-protocol
- #niagara
- #open-source
- #python
- #ue5
- #unreal-engine
- #unreal-engine-plugin
- #vibe-coding
About this skill
Pulled from SKILL.md at publish time.
> 🧠 **Brains complement:** IF an `unreal-engine-skills-manager` tool (external MCP) exists in this session, call it with `{action: "load", skill: "audio-and-metasounds"}` 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.
Evaluation report
WarningsAutomated checks the publisher passed at publish time — structure, docs, safety, and whether the artifact behaves as claimed.87ec7e6· 2 weeks ago
Kind-specific
31Skill: SKILL.md present
found at Content/Skills/metasounds/SKILL.md · frontmatter source: SKILL.md
Skill: body content present
2,763 words · 19,629 chars · 31 sections · 15 code blocks
Skill: triggers declaredwarn
No `trigger` phrases in SKILL.md frontmatter
Add `trigger:` lines so Claude knows when to activate this skill — e.g. `when building MCP servers` or `for diagram creation`.
Skill: allowed-tools scope
no allowed-tools restriction (Claude may use anything)
Release history
1- releasecurrent87ec7e6warn2 weeks ago
Contents
🧠 Brains complement: IF an
unreal-engine-skills-managertool (external MCP) exists in this session, call it with{action: "load", skill: "audio-and-metasounds"}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.
MetaSound Service Skill
Use this skill to create and edit MetaSound Source assets via Python.
import unreal
ms = unreal.MetaSoundService()
Key Concepts
- MetaSound Source — a procedural audio asset (
UMetaSoundSource) defined by a node graph. It replaces SoundCue for runtime-parameterisable sounds. - Node — a DSP processing block (Sine oscillator, Gain, Delay, etc.). Nodes have named input pins and output pins with associated DataTypes.
- NodeId — a GUID string returned by
add_node. Pass it to connect, remove, set_default, etc. - Graph I/O —
add_graph_input/add_graph_outputexpose values at the asset level (settable at runtime viaSet Float Parameter, etc.). - Standard interface — every Source created by this service comes pre-wired with:
On Play(Trigger output) — fires when the sound startsOn Finished(Trigger input) — call to stop the soundAudio:0(Audio input on the graph output node) — connect your audio signal here
Workflow
1 — Discover available nodes
# List all nodes whose class name or display name contains "Sine"
nodes = ms.list_available_nodes("Sine")
for n in nodes:
print(n.full_class_name, " inputs:", n.inputs, " outputs:", n.outputs)
2 — Create a MetaSound
r = ms.create_meta_sound("/Game/Audio", "MS_SineLoop", "Mono")
asset_path = r.asset_path # "/Game/Audio/MS_SineLoop"
3 — Find the built-in interface node IDs
all_nodes = ms.list_nodes(asset_path)
for n in all_nodes:
print(n.node_id, n.node_title, n.inputs, n.outputs)
# IMPORTANT: A MetaSound Source has MULTIPLE nodes with title "Output" —
# one per interface pin group (e.g. "On Finished" Trigger, "Out Mono" Audio).
# You MUST filter by the node whose inputs contain an Audio-type pin.
# Pin strings are "VertexName:TypeName" — the TypeName suffix after the last colon.
audio_out_node = next(
n for n in all_nodes
if n.node_title == "Output" and any(p.endswith(":Audio") for p in n.inputs)
)
audio_out_id = audio_out_node.node_id
# Drop the last :TypeName suffix to get the raw vertex name for connect_nodes
audio_in_pin = ":".join(audio_out_node.inputs[0].split(":")[:-1]) # "UE.OutputFormat.Mono.Audio:0"
# Input node — filter by class_name "Input.Trigger" to avoid matching graph input nodes
# (graph inputs also appear as "Input" nodes but with class "Input.Float", "Input.Bool", etc.)
input_node = next(n for n in all_nodes if n.class_name == "Input.Trigger")
input_node_id = input_node.node_id
on_play_pin = ":".join(input_node.outputs[0].split(":")[:-1]) # "UE.Source.OnPlay"
4 — Add a node
# add_node(asset_path, namespace, name, variant="", major_version=1, pos_x=0, pos_y=0)
# Use list_available_nodes() to discover the correct namespace/name/variant for any node
r2 = ms.add_node(asset_path, "UE", "Sine", "Audio", 1, -200.0, 0.0)
sine_id = r2.node_id # GUID string
5 — Set a node input default
# Set the Sine frequency to 880 Hz
# Use get_node_pins() to confirm exact input pin names before setting
ms.set_node_input_default(asset_path, sine_id, "Frequency", "880.0", "Float")
# Read it back (issue #460):
ms.get_node_input_default(asset_path, sine_id, "Frequency") # -> "880.000000"
# All inputs at once: name / data_type / default_value / is_connected
for v in ms.get_node_input_values(asset_path, sine_id):
print(v.name, v.data_type, v.default_value, v.is_connected)
6 — Connect nodes
# connect_nodes(asset_path, from_node_id, output_name, to_node_id, input_name)
# Sine output pin is "Audio"; AudioOut input pin is "UE.OutputFormat.Mono.Audio:0"
ms.connect_nodes(asset_path, sine_id, "Audio", audio_out_id, audio_in_pin)
# Read the wiring back (issue #460) — every edge touching a node, both directions:
for c in ms.get_node_connections(asset_path, sine_id):
print(f"{c.from_node_id}.{c.from_output} -> {c.to_node_id}.{c.to_input} [{c.data_type}]")
7 — Save
ms.save_meta_sound(asset_path)
Method Reference
Lifecycle
| Method | Description |
|---|---|
create_meta_sound(package_path, asset_name, output_format="Mono") | Create a new MetaSound Source asset. Returns FMetaSoundResult with asset_path. |
delete_meta_sound(asset_path) | Delete a MetaSound asset. |
get_meta_sound_info(asset_path) | Return FMetaSoundInfo (node count, output format, graph I/O names). |
save_meta_sound(asset_path) | Save after edits. Always call after making graph changes. |
Node Discovery
| Method | Description |
|---|---|
list_available_nodes(search_filter="") | List all registered External/DSP node classes. Returns TArray<FMetaSoundNodeClassInfo>. Each entry has full_class_name, namespace, name, variant, inputs, outputs, display_name. |
Node Management
| Method | Returns | Description |
|---|---|---|
add_node(asset_path, namespace, name, variant="", major_version=1, pos_x=0, pos_y=0) | FMetaSoundResult | Add a node. node_id on result is the GUID string. |
remove_node(asset_path, node_id) | FMetaSoundResult | Remove node and all its edges. |
list_nodes(asset_path) | TArray<FMetaSoundNodeInfo> | List all nodes in the graph. |
get_node_pins(asset_path, node_id) | FMetaSoundNodeInfo | Return pin info for a single node. |
Connections
| Method | Returns | Description |
|---|---|---|
connect_nodes(asset_path, from_node_id, output_name, to_node_id, input_name) | FMetaSoundResult | Connect an output pin to an input pin. Check r.success. |
disconnect_pin(asset_path, node_id, input_name) | FMetaSoundResult | Remove the connection going into an input pin. |
Graph I/O
| Method | Returns | Description |
|---|---|---|
add_graph_input(asset_path, input_name, data_type, default_value="") | FMetaSoundResult | Add a named input exposed as a runtime parameter. Appears as an Input.<Type> node in the graph. |
add_graph_output(asset_path, output_name, data_type) | FMetaSoundResult | Add a named output. |
remove_graph_input(asset_path, input_name) | FMetaSoundResult | Remove a graph input. |
remove_graph_output(asset_path, output_name) | FMetaSoundResult | Remove a graph output. |
Node Configuration
| Method | Returns | Description |
|---|---|---|
set_node_input_default(asset_path, node_id, input_name, value, data_type) | FMetaSoundResult | Set a literal default on a node input. data_type: "Float", "Int32", "Bool", "String", "WaveAsset". |
set_node_location(asset_path, node_id, pos_x, pos_y) | FMetaSoundResult | Update editor position. |
Common DataTypes
| Type name | Description |
|---|---|
Float | 32-bit float (frequency, gain, time) |
Int32 | Integer |
Bool | Boolean |
String | Text string |
Audio | Audio signal (mono channel) |
Trigger | Impulse / event signal |
Time | Duration in seconds (use Float in most cases) |
WaveAsset | Reference to a SoundWave asset |
Complete Example — 880 Hz Sine Tone
import unreal
ms = unreal.MetaSoundService()
# Create asset
r = ms.create_meta_sound("/Game/Audio", "MS_Test880Hz", "Mono")
ap = r.asset_path
# Find the AudioOut node — there are multiple nodes titled "Output" (one per interface
# pin group). Filter for the one whose inputs contain an Audio-type pin.
nodes = ms.list_nodes(ap)
audio_out_node = next(
n for n in nodes
if n.node_title == "Output" and any(p.endswith(":Audio") for p in n.inputs)
)
audio_out_id = audio_out_node.node_id
# Pin strings are "VertexName:TypeName" — drop only the last :TypeName to get the vertex name
audio_in_pin = ":".join(audio_out_node.inputs[0].split(":")[:-1]) # "UE.OutputFormat.Mono.Audio:0"
# Add Sine oscillator (use list_available_nodes("Sine") to discover namespace/name/variant)
r2 = ms.add_node(ap, "UE", "Sine", "Audio", 1, -300.0, 0.0)
sine_id = r2.node_id
# Set frequency to 880 Hz (use get_node_pins() to confirm exact pin names)
ms.set_node_input_default(ap, sine_id, "Frequency", "880.0", "Float")
# Connect Sine "Audio" output to the AudioOut sink pin
ms.connect_nodes(ap, sine_id, "Audio", audio_out_id, audio_in_pin)
# Save
ms.save_meta_sound(ap)
print("Done:", ap)
Complete Example — One-Shot SoundWave (Gunshot)
import unreal
ms = unreal.MetaSoundService()
# Create a Mono MetaSound Source
r = ms.create_meta_sound("/Game/Audio", "MS_Gunshot", "Mono")
ap = r.asset_path
# Find interface nodes
nodes = ms.list_nodes(ap)
# Input node — has "On Play" Trigger output
input_node = next(n for n in nodes if n.node_title == "Input")
input_node_id = input_node.node_id
# Audio Output node — filter for the one with an Audio-type input
audio_out_node = next(
n for n in nodes
if n.node_title == "Output" and any(p.endswith(":Audio") for p in n.inputs)
)
audio_out_id = audio_out_node.node_id
audio_in_pin = ":".join(audio_out_node.inputs[0].split(":")[:-1])
# Add Wave Player (Mono) node
# Pin names (no need to call get_node_pins — see Known Node Pins below):
# Inputs: "Play" (Trigger), "Stop" (Trigger), "Wave Asset" (WaveAsset), "Loop" (Bool)
# Outputs: "Out Mono" (Audio), "On Play" (Trigger), "On Finished" (Trigger)
wp = ms.add_node(ap, "UE", "Wave Player", "Mono", 1, -300.0, 0.0)
wp_id = wp.node_id
# Set the wave asset
ms.set_node_input_default(ap, wp_id, "Wave Asset", "/Game/Audio/SW_Gunshot_01", "WaveAsset")
# Wire On Play (Input) → Play (WavePlayer)
# CRITICAL: use the vertex name "UE.Source.OnPlay" — NOT the display name "On Play"
r = ms.connect_nodes(ap, input_node_id, "UE.Source.OnPlay", wp_id, "Play")
if not r.success: raise RuntimeError(f"connect On Play→Play failed: {r.message}")
# Wire Out Mono (WavePlayer) → audio sink (Output)
r = ms.connect_nodes(ap, wp_id, "Out Mono", audio_out_id, audio_in_pin)
if not r.success: raise RuntimeError(f"connect Out Mono→Output failed: {r.message}")
# Save
ms.save_meta_sound(ap)
print("Done:", ap)
Return Type Attribute Reference
Use these exact attribute names — guessing leads to AttributeError.
FMetaSoundResult — returned by most mutating methods
| Attribute | Type | Example |
|---|---|---|
success | bool | True |
message | str | "Connected A.Audio -> B.UE.OutputFormat.Mono.Audio:0" |
asset_path | str | "/Game/Audio/MS_Test" |
node_id | str (GUID) | "A1B2C3D4-..." (populated by add_node only) |
r = ms.connect_nodes(ap, from_id, "Audio", to_id, pin)
if not r.success:
raise RuntimeError(r.message)
# NOT r.b_success — that attribute does not exist
FMetaSoundInfo — returned by get_meta_sound_info()
| Attribute | Type | Example |
|---|---|---|
asset_path | str | "/Game/Audio/MS_Test_Blip" |
asset_name | str | "MS_Test_Blip" |
output_format | str | "Mono" |
node_count | int | 4 |
graph_inputs | list[str] | [] |
graph_outputs | list[str] | [] |
info = svc.get_meta_sound_info(asset_path)
print(info.node_count, info.output_format)
# NOT info.success, info.b_success, info.message
FMetaSoundNodeInfo — returned by list_nodes() and get_node_pins()
| Attribute | Type | Example |
|---|---|---|
node_id | str (GUID) | "A1B2C3D4-..." |
node_title | str | "Wave Player" |
class_name | str | "UE.Wave Player.Mono" |
inputs | list[str] | ["Play:Trigger", "Wave Asset:WaveAsset"] |
outputs | list[str] | ["Out Mono:Audio", "On Finished:Trigger"] |
nodes = ms.list_nodes(asset_path)
for n in nodes:
print(n.node_id, n.node_title, n.class_name)
# NOT n.node_class, n.node_class_name, n.name
FMetaSoundNodeClassInfo — returned by list_available_nodes()
| Attribute | Type | Example |
|---|---|---|
full_class_name | str | "UE.Wave Player.Mono" |
namespace | str | "UE" |
name | str | "Wave Player" |
variant | str | "Mono" |
display_name | str | "Wave Player (Mono)" |
inputs | list[str] | ["Play:Trigger", ...] |
outputs | list[str] | ["Out Mono:Audio", ...] |
nodes = ms.list_available_nodes("Wave Player")
for n in nodes:
print(n.name, n.variant, n.full_class_name)
# NOT n.node_name, n.node_class, n.class_name
Known Node Pins
Use these instead of calling get_node_pins on freshly-added nodes (can time out).
Wave Player (UE.Wave Player.Mono)
| Direction | Pin | Type |
|---|---|---|
| Input | Play | Trigger |
| Input | Stop | Trigger |
| Input | Wave Asset | WaveAsset |
| Input | Loop | Bool |
| Input | Pitch Shift | Float |
| Input | Start Time | Time |
| Input | Loop Start | Time |
| Input | Loop Duration | Time |
| Input | Maintain Audio Sync | Bool |
| Output | Out Mono | Audio |
| Output | On Play | Trigger |
| Output | On Finished | Trigger |
| Output | On Nearly Finished | Trigger |
| Output | On Looped | Trigger |
| Output | On Cue Point | Trigger |
| Output | Cue Point ID | Int32 |
| Output | Cue Point Label | String |
| Output | Loop Percent | Float |
| Output | Playback Location | Float |
| Output | Playback Time | Time |
Sine (UE.Sine.Audio)
| Direction | Pin | Type |
|---|---|---|
| Input | Frequency | Float |
| Input | Modulation | Audio |
| Input | Enabled | Bool |
| Input | Bi Polar | Bool |
| Input | Sync | Trigger |
| Input | Phase Offset | Float |
| Input | Glide | Float |
| Input | Type | Enum:SineGenerationType |
| Output | Audio | Audio |
Standard Interface Nodes
CRITICAL: Interface node pins use namespaced vertex names. The display name shown
in the editor is NOT the vertex name. Always use the vertex names below in connect_nodes.
| Node title | Vertex name (EXACT string for connect_nodes) | Type | Direction |
|---|---|---|---|
Input | UE.Source.OnPlay | Trigger | Output |
Output (Trigger) | UE.Source.OneShot.OnFinished | Trigger | Input |
Output (Audio, Mono) | UE.OutputFormat.Mono.Audio:0 | Audio | Input |
Output (Audio, Stereo Left) | UE.OutputFormat.Stereo.Audio:0 | Audio | Input |
Output (Audio, Stereo Right) | UE.OutputFormat.Stereo.Audio:1 | Audio | Input |
Output (Audio, Quad 0–3) | UE.OutputFormat.Quad.Audio:0 … :3 | Audio | Input |
The audio output format depends on the output_format passed to create_meta_sound
("Mono" / "Stereo" / "Quad"). A Stereo source has two Output.Audio nodes
(Left :0, Right :1); a Quad source has four. Mono has one.
Connecting to a Stereo (or Quad) output
A Mono asset has a single audio sink, so next(...) is fine. Stereo/Quad assets have one
audio output node per channel — grab them all and feed each one, or channels will be silent.
# Collect every audio sink node (works for Mono, Stereo, and Quad)
nodes = ms.list_nodes(ap)
audio_out_nodes = [
n for n in nodes
if n.node_title == "Output" and any(p.endswith(":Audio") for p in n.inputs)
]
# Sort by channel index so [0]=Left, [1]=Right (the vertex name ends with ":<index>:Audio")
audio_out_nodes.sort(key=lambda n: n.inputs[0])
# Mono: one node. Stereo: two (Left, Right). Quad: four.
# Example — send a mono signal (e.g. a Mixer "Out") to BOTH stereo channels:
for out_node in audio_out_nodes:
sink_pin = out_node.inputs[0] # e.g. "UE.OutputFormat.Stereo.Audio:0:Audio"
r = ms.connect_nodes(ap, source_id, "Out", out_node.node_id, sink_pin)
if not r.success: raise RuntimeError(r.message)
For a true stereo image, drive Left and Right from different signals (e.g. two oscillators,
or a stereo Audio Mixer (Stereo, N) whose Out L / Out R feed the two sinks). To get a
stereo mixer, search list_available_nodes("Mixer") for a (Stereo, …) variant — the
(Mono, N) mixer has a single Out pin and only fills one channel on its own.
connect_nodesaccepts the full"VertexName:TypeName"pin string (it strips the trailing:TypeName), so you can passn.inputs[0]directly without manual splitting.
Notes
- Save after every batch of edits, not after each individual operation, to avoid excessive disk I/O.
list_available_nodes("")returns all registered node classes (~400+). Use a filter like"Sine","Delay","Gain"to narrow the results.- Node pin names for standard nodes use UE display names (e.g.
"In Frequency","Out","Audio:0"). Useget_node_pins()to confirm exact names. - A MetaSound Source has multiple nodes with
node_title == "Output"— one per interface pin group (e.g."On Finished"Trigger,"Out Mono"Audio). To find the audio sink node, filter for the Output node whose inputs contain an Audio-type pin:next(n for n in nodes if n.node_title == "Output" and any(p.endswith(":Audio") for p in n.inputs)). Pin strings fromlist_nodesare"VertexName:TypeName"— you can pass them directly toconnect_nodes, which now automatically strips the trailing:TypeNamesuffix. Manual stripping (":".join(pin.split(":")[:-1])) still works but is no longer required. - Node namespace/name/variant values differ from what the MetaSound editor displays. Always call
list_available_nodes("keyword")to discover the correct values; do not guess. - MetaSound Sources do not support SoundCue-style
SoundNodeWavePlayer— use theWavePlayerMetaSound node instead (discover vialist_available_nodes("Wave Player")). - Graph inputs appear as
Inputnodes in the graph (class_name == "Input.Float","Input.Bool", etc.). When filtering for the standard interface Input node (the one that carriesOn Play), always match byclass_name == "Input.Trigger"— not bynode_title == "Input"alone, which will also match graph input nodes. - AudioMixer pins interleave audio and gain:
Audio Mixer (Mono, 2)has pins["In 0:Audio", "Gain 0:Float", "In 1:Audio", "Gain 1:Float"]. Never index by position — always filter for:Audiotyped pins when connecting audio signals:audio_ins = [p.split(":")[0] for p in mixer_node.inputs if p.endswith(":Audio")]. - Stereo MetaSound Sources have TWO audio output sinks, one per channel. A Stereo source's audio output pins are
UE.OutputFormat.Stereo.Audio:0(Left) andUE.OutputFormat.Stereo.Audio:1(Right) — exposed as two separateOutput.Audionodes, not one. (Quad has four::0–:3.) You must feed both channels or one side will be silent. The single-next()audio-sink finder used for Mono only grabs one channel — for Stereo, collect every audio output node. See Connecting to a Stereo/Quad output below.
Sample scripts (run via execute_python_code)
scripts/create_metasound.txt— create a MetaSound Source and list available node types.
Reviews
No reviews yet. Be the first.
Related
Gpt Researcher
An autonomous agent that conducts deep research on any data using any LLM providers
Browser Use
🌐 Make websites accessible for AI agents. Automate tasks online with ease.
Frontend Slides
Create beautiful slides on the web using Claude's frontend skills
mh install skills/metasounds