$npx -y skills add tebjan/vvvv-skills --skill vvvv-custom-nodesHelps write C# node classes for vvvv gamma — the [ProcessNode] lifecycle pattern, Update() method, out parameters, pin configuration, change detection, stateless operation nodes, the public-API import model, and service consumption via NodeContext (IFrameClock, Game access, loggi
| 1 | # Writing Custom Nodes for vvvv gamma |
| 2 | |
| 3 | ## What `[ProcessNode]` actually does (and what it does NOT do) |
| 4 | |
| 5 | **Important reality check.** vvvv imports every `public` class, struct, enum, method, and property from the assembly's configured namespaces — `[ProcessNode]` does NOT control whether a class becomes a node. A plain `public class Foo { public int Bar(int x) => x; }` will appear in the node browser exactly the same way as one decorated with `[ProcessNode]`. The attribute affects *how* the runtime treats the class, not *whether* it gets imported. |
| 6 | |
| 7 | What `[ProcessNode]` DOES: |
| 8 | |
| 9 | - Tells vvvv "this is a stateful node — keep ONE instance alive per node in the patch and call its `Update()` method each frame". |
| 10 | - Lets you set `Name = "..."` (rename for the node browser), `Category = "..."` (override the assembly's category), and `HasStateOutput = true` (expose the instance as an output pin). |
| 11 | - Engages live reload, `IDisposable` cleanup, and the `NodeContext` constructor injection. |
| 12 | |
| 13 | What `[ProcessNode]` does NOT do: |
| 14 | |
| 15 | - It does NOT make a class visible to vvvv. Visibility comes from the C# `public` access modifier plus an `[assembly: ImportAsIs/ImportNamespace/ImportType]` attribute. See vvvv-node-libraries for the import rules. |
| 16 | - It does NOT hide internal helpers. If a helper is `public`, it's a node — period. Use `internal` to hide. |
| 17 | - It does NOT change pin generation. Pin generation is driven by the `Update()` method signature for `[ProcessNode]` classes, and by public method/property signatures for non-`[ProcessNode]` classes. |
| 18 | |
| 19 | **When to use `[ProcessNode]`:** the class needs frame-by-frame updates, persistent state between frames, or `IDisposable` cleanup. **When to skip `[ProcessNode]`:** stateless utility classes, value types, static helper methods — leaving the attribute off avoids the per-frame `Update()` ceremony, and the public methods become operation nodes automatically. |
| 20 | |
| 21 | ## ProcessNode Pattern — The Core Pattern |
| 22 | |
| 23 | The canonical stateful C# node in vvvv gamma: |
| 24 | |
| 25 | ```csharp |
| 26 | [ProcessNode] |
| 27 | public class MyTransform : IDisposable |
| 28 | { |
| 29 | private float _lastInput; |
| 30 | private float _cachedResult; |
| 31 | |
| 32 | /// <summary> |
| 33 | /// Transforms input values with caching. |
| 34 | /// </summary> |
| 35 | public void Update( |
| 36 | out float result, // OUT parameters FIRST |
| 37 | out string error, // More out params |
| 38 | float input = 0f, // Value inputs with defaults AFTER |
| 39 | bool reset = false) |
| 40 | { |
| 41 | error = null; |
| 42 | |
| 43 | if (input != _lastInput || reset) |
| 44 | { |
| 45 | _cachedResult = ExpensiveComputation(input); |
| 46 | _lastInput = input; |
| 47 | } |
| 48 | |
| 49 | result = _cachedResult; // ALWAYS output cached data |
| 50 | } |
| 51 | |
| 52 | public void Dispose() { /* cleanup */ } |
| 53 | } |
| 54 | ``` |
| 55 | |
| 56 | **Prerequisite**: vvvv only sees a `[ProcessNode]` class (or any other public class) if its namespace is covered by an assembly-level import attribute — `[assembly: ImportAsIs]`, `[assembly: ImportNamespace]`, or `[assembly: ImportType]` for that specific class. Without one of those, the assembly's public API is invisible to the node browser entirely. Projects scaffolded by vvvv include `[assembly: ImportAsIs]` automatically. For multi-namespace libraries or hand-picked node lists, see vvvv-node-libraries — `ImportAsIs` is `AllowMultiple = false`, so you'll reach for `ImportNamespace` (multi-use) or `ImportType` (per-type) when one root attribute isn't enough. |
| 57 | |
| 58 | ### Non-Negotiable Rules |
| 59 | |
| 60 | 1. **`[ProcessNode]` attribute** on every stateful node class |
| 61 | 2. **No "Node" in the vvvv-visible name** — everything in vvvv is already a node, so "Node" suffix is redundant |
| 62 | 3. **`out` parameters FIRST**, value inputs with defaults AFTER |
| 63 | 4. **XML comments** on class and Update method (shown as tooltip in vvvv) |
| 64 | 5. **ZERO allocations in Update** — no `new`, no LINQ, cache everything |
| 65 | 6. **Change detection** — only recompute when inputs actually change |
| 66 | 7. **Always output latest data** — even when no work is done, output cached result |
| 67 | 8. **No unnecessary public members** — data flows through Update in/out params only |
| 68 | 9. **`IDisposable`** for any node holding native/unmanaged resources |
| 69 | |
| 70 | ### Live Reload Behavior |
| 71 | |
| 72 | When the .v |