Skip to main content
Shader Graph is a visual scripting tool that compiles to shader code. It is ideal whether you have never touched a shader language before or you want to quickly prototype shader ideas without writing HLSL by hand.

Opening the Shader Graph editor

1

Create a Shader Graph asset

Open the Asset Browser, right-click, and select Create > Shader Graph. Give the asset a name and save it.
2

Open the editor

Double-click the Shader Graph asset to open the Shader Graph Editor window.

Editor layout

The editor is divided into four panels:
PanelPurpose
PreviewShows a real-time preview of your shader output. Change the preview model and lighting to suit your shader.
PropertiesInspect and edit properties of the shader or any selected node.
GraphThe node canvas. All graphs end with a Material node where your output values connect.
OutputDisplays errors and warnings. Also contains tabs for the Console, Undo History, and Node Palette.
You can drag and reposition each panel however you like.

Creating nodes

Right-click anywhere in the Graph canvas to open the node creation menu, or drag nodes from the Palette tab in the Output panel directly onto the canvas.

Input and output types

Each node pin is colour-coded by type:
ColourTypeExample uses
GreenFloatA single floating-point value
PurpleFloat2UV coordinates, screen position
BlueFloat3Positions, normals, RGB colour
YellowFloat4 / ColorRGBA, positions with W component
Some nodes adapt their output type based on their inputs. For example, a Multiply node returns a Float when given two Float inputs, but returns a Float4 when a Color is connected.

Turning a Shader Graph into a material

When you save your Shader Graph, s&box compiles it and places a .shader file alongside it. You can use that shader file when creating a Material in the Asset Browser. To create a material directly from the graph, right-click the compiled shader asset and select Create Material.

Custom nodes

You have two ways to extend the node library with your own reusable nodes.

Subgraphs

Package a group of existing nodes into a single reusable node. Good for encapsulating logic you repeat across shaders.

C# Shader Nodes

Write a ShaderNode subclass in C# to define fully custom inputs, outputs, and generated HLSL.

Creating a subgraph

A Subgraph (Shader Graph Function) is a collection of nodes saved as a standalone asset that other graphs can use as a single node.
1

Create the asset

In the Asset Browser, right-click and select Create > Shader > Shader Graph Function.
2

Or extract from an existing graph

Select the nodes you want to extract in an open graph, right-click, and choose Create Custom Node.
Defining outputs — the Result node in a subgraph works like the Material node in a full shader. Add as many outputs as you need via the Properties panel and choose which to display in the Preview. Defining inputs — add a Subgraph Input node to expose a pin on the resulting node. Each input has a name, type, default value, and an order that controls pin position. Set Is Required to true to force the caller to connect a node rather than use the default value.
Any Texture nodes used inside a subgraph must be named. Textures cannot be baked into the subgraph because any material using the compiled shader needs to supply its own textures.

Creating a C# shader node

Custom nodes inherit from ShaderNode and support [Title], [Description], [Category], and [Icon] attributes.
[Title("My Custom Node"), Category("Custom"), Icon("favorite")]
public class MyCustomNode : ShaderNode
{
    // ...
}
Defining inputs — declare NodeInput properties with the [Input] attribute. Use [InputDefault] to expose a default value in the Properties panel.
public class MyMathNode : ShaderNode
{
    [Input(typeof(float))]
    [Title("Value A")]
    public NodeInput InputA { get; set; }

    [Input(typeof(Vector3))]
    [Title("Direction")]
    public NodeInput Direction { get; set; }

    [InputDefault(nameof(InputA))]
    public float DefaultA { get; set; } = 1.0f;
}
Defining outputs — declare NodeResult.Func properties with the [Output] attribute. Use the GraphCompiler to resolve connected inputs and emit HLSL.
[Output(typeof(float))]
[Title("Result")]
public NodeResult.Func Result => (GraphCompiler compiler) =>
{
    var a = compiler.Result(InputA, DefaultA);
    var dir = compiler.Result(Direction, Vector3.Forward);

    return new NodeResult(1, $"dot({a}, {dir}.x)");
};
Dynamic inputs and outputs — override Inputs and Outputs to return lists you build at runtime, for example when the number of pins depends on a property the user configures.
public class DynamicNode : ShaderNode
{
    [Hide]
    private List<IPlugIn> _dynamicInputs = new();

    [Hide]
    private List<IPlugOut> _dynamicOutputs = new();

    public override IEnumerable<IPlugIn> Inputs => _dynamicInputs;
    public override IEnumerable<IPlugOut> Outputs => _dynamicOutputs;

    public void AddInput(string name, Type type)
    {
        var info = new PlugInfo()
        {
            Name = name,
            Type = type,
            DisplayInfo = new DisplayInfo { Name = name }
        };
        _dynamicInputs.Add(new BasePlugIn(this, info, type));
    }
}
Validation — implement IErroringNode to return a list of error strings that prevent the graph from compiling until the user fixes them.
public class ValidatedNode : ShaderNode, IErroringNode
{
    [Input(typeof(float))]
    public NodeInput Value { get; set; }

    public List<string> GetErrors()
    {
        var errors = new List<string>();
        if (!Value.IsValid)
            errors.Add("Value input is required");
        return errors;
    }
}
Custom node rendering — override OnPaint to draw custom visuals on the node face, similar to built-in Binary and Texture nodes.
public class VisualNode : ShaderNode
{
    public override void OnPaint(Rect rect)
    {
        Paint.SetPen(Color.White);
        Paint.SetFont("Arial", 12);
        Paint.DrawText(rect, "Custom");
    }
}