A Pangur Primer

Pangur is a visual programming language for working with text in real time. If you have ever used a visual programming language like Max/MSP or Pure Data, many of the concepts here will seem familiar to you.

Pangur is named after a cat that lived in the ninth century.

About this guide

This guide, like Pangur itself, is a work in progress.

Terminology

A Pangur program is made of a collection of nodes. Each node receives and emits pulses, each of which has a (possibly empty) piece of text associated with it. Most nodes have one or more input channels which can receive pulses, and one or more output channels which can emit pulses. We can link an output channel of one node to an input channel of another node using a route.

Using Pangur

To add a new node to the program, right-click on the background and select Add Node, then choose the node from the menu. To remove it again, right-click on the node, and choose Remove.

You can create routes by dragging from an output channel of one node to an input channel of another (or vice versa). Each output channel can link to as many inputs as you want, but each input can only come from one output. If you need to combine multiple outputs into a single output, use the Merge node (under control).

A Bestiary of Nodes

Nodes are grouped according to how they treat their inputs and outputs.

We have:

The simplest possible Pangur program consists of a Start node, some sort of source, and then an output node:

the simplest possible Pangur program

You probably want to do something more interesting than this though! So you'll need some of the more interesting node types.

A common pattern is to take a pulse, split it, feed the resulting sequence through some combination of transmute, branch and gather, then re-join the sequence at the end. We often re-use the original pulse as a trigger for the gather and join steps.

an example of the split-gather-join pattern

In this example, we use Split Words to break each line into separate words (ignoring the punctuation and whitespace pulses). Then each word passes separately through Anagram, before being fed into the input of Join With Spaces. The original pulse containing the line of poetry is routed into the "Trigger" input of Join With Spaces, which stitches the anagrammed words back together into a line.

Looping

Clicking repeatedly to run your program can get slightly tiresome, so it's often useful to have a way of looping things. The obvious idea is to just loop around, so the end of your program triggers the start:

an example of a loop with the end of a program triggering the start

If you try this example, you'll find that this doesn't work. The problem is that this would create a kind of infinite loop (in programming terms, a "stack overflow"), and Pangur is smart enough to prevent it from happening.

The solution is to use the Loop node. This has two input channels, "Start/Stop", and "Continue". The "Start/Stop" channel toggles the loop between running and not running. When the loop is running, pulses which come in to the "Continue" channel are passed through to the output channel, but with enough of a delay that the overall rate is kept below a certain threshold (by default 120 loops per minute, or about one every half second).

an example using the loop node

In this version of the previous example, the infinite loop problem is solved.

Source material

There is a selection of different source texts available under the source menu. If none of these are to your tastes, you can try the Search for Poem node. This will search for you on the Poetry Foundation website, and return the first hit.

an example using Book 1 of Paradise Lost

In this example we take a particularly long source text, and alphabetise each line for ease of reference.

If even that isn't enough, you can load a text file of your choosing with the Custom Text node.

Up until now we've been using the first (unlabeled) input channel on each source, which causes the node to emit text a line at a time. Usually this is what we want, so we can pace our movement through a source text. Sometimes though, we want to do something more complex.

an example of randomly selecting a line from a poem

In this example we use the second input channel (labeled "All") to load all of the lines at once into a Random Choice node, and then trigger that node, causing it to emit a random line from the poem.

Order of operations

When a node emits more than one pulse (e.g. from a split node), each pulse propagates fully before the next one is handled.

If an output channel is connected to more than one input, the same rule applies: each of the routes is handled sequentially. The order depends on the order in which the routes were created - oldest routes are handled first. This is not currently obvious from the interface.

Sometimes it's useful to be more explicit about the order in which things happen. In these cases, the Sequence node is particularly useful. This node repeats its input across up to four output channels, in order from left to right.

an example of the Sequence node in action

In this example, whenever we click the Start node, the pulse goes first to Count, then to A Dadaist Poem, and finally to Join With Spaces. By the time the triggering pulse reaches Join With Spaces, its input has already been loaded up by the previous two nodes.

If multiple pulses are emitted over multiple routes (whoa there tiger), then each of the routes for the first pulse are handled (in order), then each of the routes for the second pulse, and so on.

In practice, these defaults are usually what you want. If you need to rearrange the order of pulses, then the Hold node is particularly useful. This node holds a queue of pulses which it receives through its first input channel, and releases them one by one when it receives a trigger through its second input channel.

Regular Expressions

Many of the nodes use "regular expressions" to match patterns in text. A regular expression (often abbreviated to "regex") is a programming shorthand for describing combinations of characters. They can be quite terse and confusing!

an exmaple of using regular expressions in Pangur

In this example, we use the Regex Replace node to swap all of the (lower case) vowels in each line for the letter 'o'. In its simplest form, Regex Replace acts as a simple find/replace. Here, however, we're using a "character class". The "Find" parameter "[aeiou]" matches any of the five vowels.

There are a few different variations on syntax for regular expressions; Pangur uses your browser's built-in Javascript regular expression engine. You can find a good guide to its capabilities here.