Language User manual
Kraft: Portable DSL for Writing Readable Node Graphs
Kraft is a lightweight programming language to describe node graphs. First use case is the Blender Node System
This manual shows the syntax you use to declare nodes, connect sockets, reuse groups & import shared files.
1sync23Material {4 noise = NoiseTexture(scale = 5)5 bsdf = PrincipledBSDF()6 out = MaterialOutput()7 noise.color >> bsdf.base_color8 bsdf.bsdf >> out.surface9}Section 1
First Steps
Write your first `.kr` file
A Kraft file describes a node graph as text. Start with a preview mode, then declare the graph you want to create.
- Use the `.kr` extension for Kraft source.
- Put one entry group in the file when you want Blender to preview that graph.
- Inside the group, declare nodes first and connect their sockets after that.
- The default preview mode is `readonly`, so leaving the directive out still keeps the script in charge.
1sync23Material {4 noise = NoiseTexture(scale = 5)5 bsdf = PrincipledBSDF()6 out = MaterialOutput()7 noise.color >> bsdf.base_color8 bsdf.bsdf >> out.surface9}Choose a preview mode
Preview mode tells Blender whether the text alone owns the graph or whether graph edits can be written back into the text.
- `sync` lets graph changes to automaticly update the Kraft source code.
- if not used `readonly`is the default keeps the graph locked to the last valid script.
- Script edits are always handled first. If the text is invalid, Kraft leaves the current preview graph alone.
- Use readonly while learning syntax, then switch to sync when you want to mix text editing with graph editing.
1syncSection 2
Entry Groups
Pick the graph kind
The first word of a group chooses the kind of Blender node tree Kraft will describe. These special groups are the main entry points.
- `Material` is for shader materials — declare nodes like `PrincipledBSDF()` and `MaterialOutput()` explicitly.
- `Geometry` is for geometry node trees — declare `GeometryOutput()` for output geometry.
- `Compositor` is for compositor trees — declare `CompositorOutput()` for the composite output.
- `Texture` is for texture-style node trees and can be used without a required output connection.
1Material {2 noise = NoiseTexture(scale = 5)3 bsdf = PrincipledBSDF()4 out = MaterialOutput()5 noise.color >> bsdf.base_color6 bsdf.bsdf >> out.surface7}89Geometry {10 join = JoinGeometry()11 out = GeometryOutput()12 join.geometry >> out.geometry13}1415Compositor {16 out = CompositorOutput()17 noise = NoiseTexture(scale = 3)18 noise.color >> out.image19}2021Texture {22 noise = NoiseTexture(scale = 8)23 out = TextureOutput()24 noise.color >> out.color25}Name reusable groups
A group with a second name is reusable. A group without a second name is an entry point for that special graph kind.
- `Material tint { ... }` declares a reusable material-shaped group named `tint`.
- `Material { ... }` declares the material entry group in the current file.
- Named groups can expose inputs and outputs through boundary connections.
- Use short names when the group behaves like a small node graph function.
1Material tint {2 mix = MixRGB(3 blend_type = MULTIPLY4 )5 new_color << mix.color1 # grp input socket created6 mix.color >> new_output # grp output socket created7}89Material {10 noise = NoiseTexture(scale = 8)11 tinted = tint(new_color << noise.color)12 bsdf = PrincipledBSDF()13 out = MaterialOutput()14 tinted.new_output >> bsdf.base_color15 bsdf.bsdf >> out.surface16}Section 3
Nodes And Connections
Declare nodes
A node declaration gives a node a local name, then calls a node type or reusable group. Arguments configure properties and input socket defaults. An optional `@pos(x, y)` annotation stores the graph editor position.
- Write `name = NodeType()` to create a node.
- Use `property = value` for node properties and settable input values.
- Place `@pos(x, y)` on the line before a node declaration to store its graph editor position.
- A node name becomes the left side of later socket references such as `noise.color`.
1Material {2 @pos(100, 200)3 noise = NoiseTexture(scale = 12)4 ramp = ColorRamp()5 mix = MixRGB(6 blend_type = ADD7 color2 = Color(51, 128, 255, 255)8 )9 bsdf = PrincipledBSDF()10 out = MaterialOutput()11 noise.factor >> ramp.input12 ramp.color >> mix.color213 noise.color >> bsdf.base_color14 bsdf.bsdf >> out.surface15}Connect sockets
Connections move data from an output socket to an input socket. Kraft uses arrows so the direction is visible in the source.
- `source.output >> target.input` connects an output to an input.
- `input << source.output` exposes a group input or feeds an argument from another socket.
- Use `as "name"` after a connection when you want to preserve a link name.
- Every node you reference must be declared first — there are no implicit nodes.
1Material {2 noise = NoiseTexture(scale = 5)3 ramp = ColorRamp()4 bsdf = PrincipledBSDF()5 out = MaterialOutput()67 noise.factor >> ramp.input as "noise factor"8 ramp.color >> bsdf.base_color9 bsdf.bsdf >> out.surface10}Section 4
Groups
Expose group inputs and outputs
A reusable group can act like a node. Boundary connections decide which inputs and outputs callers can use.
- `color << mix.color1` creates a group input named `color` and feeds `mix.color1`.
- `mix.color >> output` creates a group output named `output`.
- Call the group with the same argument syntax you use for built-in nodes.
- The group body can still use regular node declarations and connections.
1Material tint {2 mix = MixRGB(color1 << color)3 mix.color >> output4}56Material {7 noise = NoiseTexture(scale = 6)8 tinted = tint(color << noise.color)9 bsdf = PrincipledBSDF()10 out = MaterialOutput()11 tinted.output >> bsdf.base_color12 bsdf.bsdf >> out.surface13}Use inline groups for local helpers
Use `Group { ... }` when a helper only matters inside one graph. The node name becomes the inline group's name.
- Inline groups avoid creating a top-level named group.
- They use the same boundary input and output rules as named groups.
- Use them for small local transforms that make the main graph easier to read.
1Material {2 tint = Group {3 mix = MixRGB(color1 << color)4 mix.color >> output5 }67 noise = NoiseTexture(scale = 9)8 tinted = tint(color << noise.color)9 bsdf = PrincipledBSDF()10 out = MaterialOutput()11 tinted.output >> bsdf.base_color12 bsdf.bsdf >> out.surface13}Section 5
Values
Set numbers, strings, booleans, and enums
Arguments accept the small value set used by Blender node properties and unlinked input sockets.
- Numbers can be integers or decimals.
- Strings use double quotes.
- Booleans are `true` and `false`.
- Enum values are written as bare uppercase names such as `MULTIPLY` or `FLOAT`. You can only use the names provided by that particular node.
1Material {2 noise = NoiseTexture(scale = 18)3 switch = Switch(type = FLOAT)4 mix = MixRGB(blend_type = MULTIPLY)5 bsdf = PrincipledBSDF()6 out = MaterialOutput()78 noise.factor >> switch.true9 switch.output >> bsdf.roughness10 bsdf.bsdf >> out.surface11}Write color values
Kraft supports common color forms so you can keep material source readable while still being precise.
- `Color(r, g, b, a)` writes numeric channels. Integer 0 to 255 (2⁸) per channel
- `Color.Name` can be used for named colors when the platform schema accepts them.
- Hex colors use `#rgb`, `#rgba`, `#rrggbb`, or `#rrggbbaa`.
- A `#` that is not a valid hex color starts a comment instead.
1Material {2 warm = MixRGB(color1 = Color(255, 89, 25, 255))3 cool = MixRGB(color1 = Color.Blue)4 flat = MixRGB(color1 = #ff8844)5 bsdf = PrincipledBSDF()6 out = MaterialOutput()7 warm.color >> bsdf.base_color8 bsdf.bsdf >> out.surface9}Store values in variables
Primitive values can be stored in named variables and reused across multiple nodes in the same scope. Enums cannot be stored — they can only be passed directly as arguments.
- `<name> = <value>` declares a variable of any primitive type: `count = 5`, `radius = 2.5`, `label = "glass"`, `enabled = true`.
- Variables are scoped to the enclosing group and can be referenced by any node declaration or connection in that group.
- Use a variable wherever a literal value is accepted: `NoiseTexture(scale = count)`.
- Enums like `MULTIPLY` or `FLOAT` cannot be stored in variables — they must be written inline.
1Material {2 scale = 53 label = "glass"4 enabled = true56 noise = NoiseTexture(scale = scale)7 bsdf = PrincipledBSDF()8 out = MaterialOutput()9 noise.color >> bsdf.base_color10 bsdf.bsdf >> out.surface11}Primitive Types Operators
Numbers support all standard arithmetic operators with standard operator precedence. Strings can be concatenated with `+`, and values are auto-converted to strings in concatenation. Booleans support logical inversion.
- Arithmetic: `+`, `-`, `*`, `/`, `%` with standard precedence. Use `( )` to group: `(10 + 5) * 2`.
- String concatenation uses `+`: `"hello" + " " + "world"` produces `"hello world"`.
- Auto-conversion in concatenation: `"value: " + 42` produces `"value: 42"` and `"got " + true` produces `"got true"`.
- Boolean inversion: `!true` is `false`, `!enabled` flips a boolean variable.
- Variables and literal values can be mixed freely: `total / 3` or `"result: " + total`.
1Material {2 total = (10 + 5) * 2 / 33 msg = "result: " + total + " " + true4 flipped = !false56 noise = NoiseTexture(scale = total)7 bsdf = PrincipledBSDF()8 out = MaterialOutput()9 noise.color >> bsdf.base_color10 bsdf.bsdf >> out.surface11}Section 6
Built-in Math Functions
Mathematical functions and constants
Kraft provides a comprehensive set of math functions and constants for use in expressions. These can be called anywhere a numeric value is expected.
- All functions return a float value.
- Constants `PI`, `TAU`, `INF`, and `NAN` are predefined.
- Functions compose: `pow(sin(x), 2) + pow(cos(x), 2)`.
1abs(x) // Absolute value of x.2sign(x) // -1, 0, or 1 depending on the sign of x.3min(a, b) // The smaller of two values.4max(a, b) // The larger of two values.5clamp(value, min, max) // Restrict a value to a range.6snapped(value, step) // Round to the nearest multiple of step.7floor(x) // Round down to nearest integer.8ceil(x) // Round up to nearest integer.9round(x) // Round to nearest integer.10fmod(a, b) // Floating-point remainder of division.11posmod(a, b) // Positive modulo result.12pow(base, exponent) // Raise a number to a power.13sqrt(x) // Square root of x.14exp(x) // e raised to the power of x.15log(x) // Natural logarithm of x.16sin(x) // Sine of an angle in radians.17cos(x) // Cosine of an angle in radians.18tan(x) // Tangent of an angle in radians.19asin(x) // Inverse sine in radians.20acos(x) // Inverse cosine in radians.21atan(x) // Inverse tangent in radians.22atan2(y, x) // Angle from X-axis to point (x, y).23deg_to_rad(degrees) // Convert degrees to radians.24rad_to_deg(radians) // Convert radians to degrees.25lerp(from, to, weight) // Linear interpolation.26inverse_lerp(from, to, val) // Interpolation factor within a range.27remap(v, i_start, i_end, o_start, o_end) // Map a value from one range to another.28smoothstep(from, to, value) // Smooth interpolation.29is_equal_approx(a, b) // Approximately equal check.30is_zero_approx(x) // Approximately zero check.31is_nan(x) // NaN check.32is_inf(x) // Infinity check.33wrapf(value, min, max) // Wrap a float into a range.34wrapi(value, min, max) // Wrap an integer into a range.35PI // 3.14159265...36TAU // 6.28318531...37INF // Positive infinity.38NAN // Not a Number.Section 7
Files And Comments
Import another Kraft file
Use imports when a workspace has shared groups or when the active entry point lives in another file.
- `from "./palette.kr" as palette` binds another file to an alias.
- `entry palette.Material` selects an imported special group as the active entry.
- Imported groups can be called with qualified names such as `palette.WarmTint()`.
- Keep aliases short and descriptive so connections stay readable.
1from "./palette.kr" as palette2entry palette.Material34Material localPreview {5 noise = NoiseTexture(scale = 4)6 tinted = palette.WarmTint(color << noise.color)7 bsdf = PrincipledBSDF()8 out = MaterialOutput()9 tinted.output >> bsdf.base_color10 bsdf.bsdf >> out.surface11}Add comments and doc comments
Comments are ignored by the graph, while doc comments can describe groups or nodes for editor tooling.
- `//` starts a regular line comment.
- `#` starts a regular line comment. Provide it not after `=` where it be comes used for hex color
- `/* ... */` writes a block comment.
- `##` and `/** ... */` are doc comments.
- Use comments for intent, not for repeating the node type already shown in the source.
1## Shared material tint used by several files.2Material tint {3 // Incoming color is multiplied with a warm accent.4 mix = MixRGB(5 blend_type = MULTIPLY6 color1 << color 7 )8 mix.color >> output9}1011/*12This entry can stay small because tint does the reusable work.13*/14Material {15 noise = NoiseTexture(scale = 5)16 tinted = tint(color << noise.color)17 bsdf = PrincipledBSDF()18 out = MaterialOutput()19 tinted.output >> bsdf.base_color20 bsdf.bsdf >> out.surface21}