# Modly Workflow Node Types: Source-Only vs Sink-Only Nodes Explained

> Explore Modly workflow node types: Understand source-only (image, text, mesh) and sink-only (output, preview) nodes for efficient data flow. Optimize your pipelines today.

- Repository: [lightningpixel/modly](https://github.com/lightningpixel/modly)
- Tags: deep-dive
- Published: 2026-08-21

---

**Modly defines five specialized node types: three source-only nodes (`imageNode`, `textNode`, `meshNode`) that initiate workflows without consuming inputs, and two sink-only nodes (`outputNode`, `previewNode`) that terminate workflows by consuming data without producing further output.**

Modly is an open-source visual workflow editor for 3D content processing maintained in the `lightningpixel/modly` repository. The application organizes processing pipelines as directed graphs where data flows from entry points through processing steps to terminal outputs. Understanding the distinction between **source-only** and **sink-only** node types is essential for constructing valid workflows that execute correctly from initiation to termination.

## Source-Only Nodes in Modly

Source-only nodes function as workflow entry points. According to the node catalog in [`src/areas/workflows/WorkflowsPage.tsx`](https://github.com/lightningpixel/modly/blob/main/src/areas/workflows/WorkflowsPage.tsx) (lines 98-100), these nodes have **no incoming edges** and serve as the starting points for any processing pipeline. The runner logic treats them as origins where data first enters the system.

### Image Source Node (`imageNode`)

The `imageNode` supplies a **local image file** that becomes the initial input for downstream processing. Defined at line 98 of [`WorkflowsPage.tsx`](https://github.com/lightningpixel/modly/blob/main/WorkflowsPage.tsx), this node is labeled "Image" in the UI palette and is implemented in [`src/areas/workflows/nodes/ImageNode.tsx`](https://github.com/lightningpixel/modly/blob/main/src/areas/workflows/nodes/ImageNode.tsx). It requires no predecessor nodes and outputs image data to connected extensions.

### Text Source Node (`textNode`)

The `textNode` provides a **text prompt** that can be fed to extensions expecting string inputs. Listed at line 99 of [`WorkflowsPage.tsx`](https://github.com/lightningpixel/modly/blob/main/WorkflowsPage.tsx) with the UI label "Text", this node is implemented in [`TextNode.tsx`](https://github.com/lightningpixel/modly/blob/main/TextNode.tsx). It allows users to input literal text values or prompts that initiate AI-driven processing steps.

### Mesh Source Node (`meshNode`)

The `meshNode` supplies **3D mesh data** including `.glb`, `.obj`, `.stl`, `.ply`, and `.splat` files, or the model currently displayed in the viewer. Defined at line 100 of [`WorkflowsPage.tsx`](https://github.com/lightningpixel/modly/blob/main/WorkflowsPage.tsx) under the label "Load 3D Mesh", this node is implemented in [`MeshNode.tsx`](https://github.com/lightningpixel/modly/blob/main/MeshNode.tsx). It serves as the entry point for geometry-based workflows.

## Sink-Only Nodes in Modly

Sink-only nodes function as workflow termination points. These nodes consume data from upstream processing but **never emit output** to subsequent steps. In [`src/areas/workflows/nodeBehaviors.ts`](https://github.com/lightningpixel/modly/blob/main/src/areas/workflows/nodeBehaviors.ts) (lines 26-28), these nodes are flagged with `sceneOutput: true`, indicating they represent final destinations in the control flow graph.

### Scene Output Node (`outputNode`)

The `outputNode` consumes a **mesh** and pushes it directly into the 3D scene when the workflow finishes. Defined at line 101 of [`WorkflowsPage.tsx`](https://github.com/lightningpixel/modly/blob/main/WorkflowsPage.tsx) with the UI label "Add to Scene", this node is implemented in [`OutputNode.tsx`](https://github.com/lightningpixel/modly/blob/main/OutputNode.tsx). The behavior registry marks this node with both `sceneOutput: true` and `branchConsumer: true`, identifying it as a terminal consumer that terminates execution branches.

### Preview Node (`previewNode`)

The `previewNode` consumes **image data** (or sets of images) and displays them in a 2×3 grid for quick inspection. Listed at line 102 of [`WorkflowsPage.tsx`](https://github.com/lightningpixel/modly/blob/main/WorkflowsPage.tsx) under the label "Preview Views" and implemented in [`PreviewNode.tsx`](https://github.com/lightningpixel/modly/blob/main/PreviewNode.tsx), this node provides visual feedback without modifying the scene graph or passing data downstream.

## How Node Behaviors Are Defined

The workflow runner's control-flow logic relies on predicates defined in [`src/areas/workflows/nodeBehaviors.ts`](https://github.com/lightningpixel/modly/blob/main/src/areas/workflows/nodeBehaviors.ts) to classify nodes. The registry uses boolean flags to determine execution behavior:

- **`isSceneOutput`**: Identifies sink-only nodes that terminate data flow
- **`isBranchConsumer`**: Marks nodes (like `outputNode`) that consume entire branches
- **`isPassthrough`** and **`isBranchStarter`**: Classify intermediate processing nodes

Only the three source nodes lack predecessors in the valid graph structure, and only the two sink nodes are flagged as scene outputs. This strict typing prevents the runner from traversing terminal nodes as intermediate processing steps.

## Programmatically Creating Source and Sink Nodes

When building workflows programmatically using the React Flow API that Modly wraps, you instantiate these node types by their string identifiers. Below is a complete example creating an image source connected to a scene output sink:

```typescript
// 1️⃣ Create a new image source node
const imageNode = {
  id: newId(),
  type: 'imageNode',          // ← source-only
  data: { /* optional metadata */ },
  position: { x: 100, y: 100 },
};

// 2️⃣ Create a sink node that will receive the mesh
const outputNode = {
  id: newId(),
  type: 'outputNode',         // ← sink-only
  data: {},
  position: { x: 400, y: 100 },
};

// 3️⃣ Wire the nodes together (image → output)
const edge = {
  id: newId(),
  source: imageNode.id,
  target: outputNode.id,
  type: 'workflowEdge',
};

setNodes((ns) => [...ns, imageNode, outputNode]);
setEdges((es) => [...es, edge]);

```

Running this workflow starts execution at `imageNode` (source) and completes when `outputNode` receives the produced data (sink). The runner validates that no edges extend from sink nodes and that source nodes have no incoming connections.

## Summary

- **Source-only nodes** (`imageNode`, `textNode`, `meshNode`) initiate workflows by supplying initial data without consuming inputs.
- **Sink-only nodes** (`outputNode`, `previewNode`) terminate workflows by consuming data and pushing results to the scene or preview grid without downstream output.
- Node classifications are defined in [`WorkflowsPage.tsx`](https://github.com/lightningpixel/modly/blob/main/WorkflowsPage.tsx) (lines 98-102) and enforced by the behavior registry in [`nodeBehaviors.ts`](https://github.com/lightningpixel/modly/blob/main/nodeBehaviors.ts) using flags like `sceneOutput` and `branchConsumer`.
- The workflow runner uses these classifications to validate graph topology and control execution flow from entry points to terminal nodes.

## Frequently Asked Questions

### What makes a node "source-only" in Modly?

A source-only node has **no incoming edges** and serves as a workflow entry point. According to the explanatory paragraphs at lines 616-632 of [`WorkflowsPage.tsx`](https://github.com/lightningpixel/modly/blob/main/WorkflowsPage.tsx), these nodes are explicitly listed in the palette as source nodes because they provide initial data (images, text, or 3D meshes) without requiring upstream processing. The runner identifies them by their lack of predecessors in the directed graph.

### Can sink-only nodes produce output for downstream processing?

No. Sink-only nodes are terminal endpoints marked with `sceneOutput: true` in [`nodeBehaviors.ts`](https://github.com/lightningpixel/modly/blob/main/nodeBehaviors.ts). They consume data from upstream nodes but do not emit values to subsequent steps. Attempting to connect an edge from a sink node like `outputNode` to another node would violate the workflow topology constraints enforced by the runner's validation logic.

### How does the workflow runner identify terminal nodes?

The runner checks the **node-behaviors registry** ([`nodeBehaviors.ts`](https://github.com/lightningpixel/modly/blob/main/nodeBehaviors.ts)) for predicates such as `isSceneOutput` and `isBranchConsumer`. Nodes flagged with these properties are treated as terminal sinks. For example, `outputNode` carries both `sceneOutput: true` and `branchConsumer: true`, signaling the runner to terminate the execution branch when reaching this node.

### Where are node types defined in the Modly codebase?

Built-in node types are cataloged in [`src/areas/workflows/WorkflowsPage.tsx`](https://github.com/lightningpixel/modly/blob/main/src/areas/workflows/WorkflowsPage.tsx) (lines 98-102) where the UI palette lists each node's type string, label, and description. The concrete implementations reside in `src/areas/workflows/nodes/` (e.g., [`ImageNode.tsx`](https://github.com/lightningpixel/modly/blob/main/ImageNode.tsx), [`OutputNode.tsx`](https://github.com/lightningpixel/modly/blob/main/OutputNode.tsx)), while execution behaviors are declared separately in [`src/areas/workflows/nodeBehaviors.ts`](https://github.com/lightningpixel/modly/blob/main/src/areas/workflows/nodeBehaviors.ts) to decouple UI presentation from runtime logic.