mediamesh · builder · product

Blocks & Mini-Graphs: Reusable Visual Compositing

Chris Mahoney

Two problems come up constantly once you’re building more than one or two channels: rebuilding the same bundle of nodes over and over (a branding overlay, an encoder ladder), and a single channel’s own graph turning into an unreadable wall of boxes. Both get solved the same way in MediaMesh: fold a chunk of graph into one reusable, droppable unit called a Block.

Reuse and tidiness are the same feature

Reach for a Block for roughly two reasons, about equally often:

  • Reuse — the same bundle of nodes, dropped onto many different channels instead of rebuilt by hand each time.
  • Tidiness — collapsing a busy chunk of one channel’s own graph into a single box, even if you never intend to reuse it anywhere else.

Author one from the Reusables panel, or select nodes on any channel canvas and save the selection directly. Drop a saved Block onto a channel from the palette or by searching for it. Each drop is its own independent copy — editing the saved Block afterward never reaches back and changes an instance you already placed on a channel. That’s deliberate: a per-channel tweak shouldn’t ripple somewhere you didn’t intend it to.

The boundary is something you draw

This is the part that separates a Block from the narrower filter-subgraph concept: a Block’s boundary isn’t inferred by the system, it’s something you draw yourself. Inside the Block editor, wire nodes together normally, then drag a connection handle onto the input or output rail along the canvas edge to expose it as one of the Block’s own external ports. Rename the resulting port if the default label isn’t descriptive enough.

That means a Block can have zero, one, or several inputs and outputs — not just exactly one of each the way a filter subgraph always does. A terminal Block that ends in an output has zero outputs. A self-contained source Block has zero inputs. The shape follows what the Block actually does, not a fixed template.

One real constraint worth knowing up front: only operators with a genuine wireable slot can expose a port at all. Something like a stream-safety wrapper or an encoder can. A pure-source operator whose “input” is really just a URL or file path typed into an Inspector field can’t — there’s no connection handle to expose, because there was never a connection there. If you hit that wall, the fix is to edit the source’s own argument inside the Block, not to go looking for a port that was never going to exist.

Six shapes, out of the box

Six reference Blocks ship with every install, and between them they cover most of the shapes a Block can actually take:

Block Shape What it does
Branding Bundle 1 in / 1 out Logo overlay, lower-third text, and a safety wrapper, composited over whatever feeds in
Standard HLS Output 1 in / 0 out The full audio/video encoder cluster wired straight into an HLS output — a terminal Block
Live + Fallback Playlist 0 in / 1 out A live feed backed by a fallback playlist, with the live URL and fallback path fixed inside the Block itself
Day-Night Scheduled Programming 0 in / 1 out Two playlists switched by time of day
ABR Encoder Ladder 1 in / 3 out One source feeding three parallel encoder tiers, each exposed as its own named output
OBS-vMix Ingest 0 in / 1 out An RTMP listener wrapped safe — point OBS or vMix at it and you’re done

The ABR Encoder Ladder is worth studying specifically if you need a multi-output shape: three parallel encoder tiers, each wired out to its own named port, ready to feed a variant list on whatever HLS output consumes it downstream.

Opening one instance never touches another

Every Block node on a canvas has an Open control on its closed card. Opening it exposes that one instance’s own copy — swapping a playlist path or tweaking an argument here changes only this placed instance, never the saved Block it came from, and never any other instance of it elsewhere. There’s no sync-back: a Block instance is a snapshot from the moment it was dropped, not a live link to the original.

That’s not an edge case to work around — it’s the expected day-to-day pattern. A different fallback playlist for one specific channel belongs in that instance, not in the saved Block. If a tweak turns out to be one you want everywhere, the answer is saving the edited instance as a new Block (versioned by name) rather than repeating the same manual edit on every channel that uses the original.

Not the same thing as a filter subgraph

Blocks and filter subgraphs solve related but genuinely different problems, and they’re easy to conflate:

  • A filter subgraph is narrower and stricter — chains built only from filter operators, always exactly one input and one output, with the boundary inferred automatically.
  • A Block is general — any node type at all, any number of ports including zero, with a boundary you draw by hand.

They nest rather than compete: a Block can contain a filter subgraph as one of its own internal nodes, using the tighter tool where it fits and the looser one everywhere else.