Appearance
Math & Logic
These are the basic building blocks for doing simple maths and making decisions inside your patch (your connected setup of nodes). The Math nodes work with numbers: they can add, multiply, compare, rescale, turn a musical note into a pitch, and more. The Logic nodes handle the "what happens next" side: they route signals, count, switch things on and off, add small delays, and react to incoming messages and events. Most of the number nodes are flexible about what you feed them: give them a plain number, a one-off event (a trigger or message), or a fast-changing control signal (an audio-rate CV signal, the kind used to move things automatically), and they adjust on their own. So the same node can do a single quick calculation or smoothly shape a moving signal moment by moment. Every node below shows its current value or result right on the node, and most let you wire other parts of your patch into their settings so those settings can be controlled automatically.
Very large numbers
The Math nodes keep every whole number exact up to 16,777,216. Above that, results are rounded, so adding or subtracting 1 can change nothing. See Number ranges and precision.
CV Edge Split
Takes an on/off signal (a gate) and splits it into two separate triggers: one fires the moment the signal turns on (note-on), and the other fires the moment it turns off (note-off). This is handy when you want the start and the end of a note to do different things.

Inputs: In (gate or boolean signal, accepts a stream or events). Outputs: On True (pulse on the rising edge), On False (pulse on the falling edge).
CV Counter
Counts each trigger it receives and sends out the running total as a steady control value. Choose to count Up, Down, or Up/Down (bouncing back and forth like a ping-pong ball). Useful for stepping through patterns, picking which item is active, or building up a value over time.

| Parameter | Range | Default | Description |
|---|---|---|---|
| Mode | Up / Down / UpDown | Up | Counting direction. Up wraps from Max back to Min, Down wraps from Min back to Max, UpDown bounces between the two. |
| Min | -1024 to 1024 | 0 | Lower bound of the counter range. |
| Max | -1024 to 1024 | 15 | Upper bound of the counter range. |
Inputs: Trigger (each rising edge advances the count), Reset (returns the count to Min, or Max in Down mode). Outputs: Count (the current value, held as a continuous CV stream).
If
This node compares two values and then sends out one of two results depending on the answer. When the comparison A op B is true, the Then output sends the Then value; when it is false, the Else output sends the Else value. For the "is equal" check there is an adjustable wiggle room, so signals that move in small fixed steps (like MIDI knob movements) still count as matching reliably.

| Parameter | Range | Default | Description |
|---|---|---|---|
| A | Any value | 0 | Left operand of the comparison. |
| B | Any value | 0 | Right operand of the comparison. |
| Op | == / != / < / <= / > / >= | == | The comparison operator. |
| Then | Any value | 1 | Value routed to Then Out when the comparison is true. |
| Else | Any value | 0 | Value routed to Else Out when the comparison is false. |
| Tol | 0 and up | 1/127 | Equality tolerance used by == and !=. Set 0 for exact float equality. Ordering operators ignore this. |
| Pass Through | On / Off | Off | Sends A itself out of whichever output the comparison picks, instead of the Then and Else values. |
Inputs: A, B, Then, Else, Op, Tol (every value input accepts a value, an event, or a CV stream). Outputs: Then Out (the Then value while the comparison is true), Else Out (the Else value while it is false).
Only the side the comparison picks sends anything, so an Event, a Trigger or a Counter wired to the other one does not fire. For a value that goes up and down, wire both outputs into the same input: Then Out sends Then while the comparison holds, Else Out sends Else while it does not, and whatever is listening always has the current answer. With CV it works differently, because there every sample carries a value: both outputs run all the time and the side that is not chosen sits at 0.
Pass Through turns the If into a switch: the signal on A goes out of Then Out while the comparison holds and out of Else Out while it does not. It is always A that travels, on either side, so it chooses where a signal goes, not which of two values to keep. For the larger or the smaller of two values, use Max or Min.
To fire something at the moment a moving signal reaches a value, such as a counter getting to 0, use CV To Event instead of comparing it here. It watches every sample and sends an event on the crossing, while a value brought over with CV To Data is only read a few dozen times a second, so an exact match can fall between two readings and a rounded one can match twice.
Logic
Combines two on/off conditions into one answer. Pick a gate: AND, OR, XOR, NOT, NAND, NOR or XNOR. An input counts as true when it is above 0 (a 1 is true, a 0 is false), which is what an If node gives when its Then Out and its Else Out both go into the same input, so you can feed A and B from two Ifs and pick AND or OR. Out is 1 when the gate is true and 0 when false. An unconnected input is ignored (AND or OR of a single wired input is just that input), and you can chain Logic nodes together for more than two conditions.

| Parameter | Range | Default | Description |
|---|---|---|---|
| Mode | AND / OR / XOR / NOT / NAND / NOR / XNOR | AND | The boolean operation applied to A and B. |
| A | Any value | 0 | First condition. Counts as true when above 0. |
| B | Any value | 0 | Second condition. Counts as true when above 0. |
Inputs: A, B, Mode (every input accepts a value, an event, or a CV stream; Mode also takes the gate by CV/Data). Outputs: Out (1 when the gate is true, 0 when false).
Addition
Adds two values together. Output = A + B. Use it to combine two control sources, shift a value up by a fixed amount, or blend signals together.

Inputs: A, B (stream-capable). Outputs: Result (A + B).
Subtract
Takes one value away from another. Output = A - B. Handy for finding the gap between two control sources, lowering a value by a fixed amount, or cancelling a signal out.

Inputs: A, B (stream-capable). Outputs: Result (A - B).
Multiply
Multiplies two values. Output = A * B. When one input is audio, it works like a volume control (turning the sound up or down). You can also use it to scale a control signal up or down.

Inputs: A, B (stream-capable). Outputs: Result (A * B).
Divide
Divides one value by another. Output = A / B. Useful for scaling things down, working out ratios, or reducing the volume by a set amount. If you try to divide by zero, the output is 0.

Inputs: A, B (stream-capable). Outputs: Result (A / B, or 0 when B is 0).
Min
Sends out whichever of the two values is smaller: Min(A, B). Useful for setting a ceiling on a signal, keeping things from going too high, or always picking the lowest of several control sources.

Inputs: A, B (stream-capable). Outputs: Result (the smaller of A and B).
Max
Sends out whichever of the two values is larger: Max(A, B). Useful for setting a floor on a signal, picking the loudest of several signals, or having one sound automatically lower another (ducking).

Inputs: A, B (stream-capable). Outputs: Result (the larger of A and B).
Power
Raises A to the power of B. Output = A^B. Good for bending a control signal into a curve, creating exponential responses, and building your own custom shapes.

Inputs: A (base), B (exponent), both stream-capable. Outputs: Result (A raised to B).
Modulo
Gives you the remainder after dividing A by B. Use it to wrap values around so they stay inside a range, build repeating ramps, or loop a control pattern. If B is zero, the output is 0.

Inputs: A, B (stream-capable). Outputs: Result (remainder of A / B, or 0 when B is 0).
Abs
Gives the absolute value: it drops the minus sign so negative numbers become positive. Handy for turning a signal that swings both above and below zero into one that only goes positive, or for getting how far a value is from zero regardless of direction.

Inputs: X (stream-capable). Outputs: Result (the absolute value of X).
Negate
Flips the sign (positive becomes negative and the other way round). Output = -X. Use it to reverse the direction of a control signal, mirror a signal, or flip its phase (turn it upside down).

Inputs: X (stream-capable). Outputs: Result (-X).
Sqrt
Gives the square root of X, and never goes below 0 so it stays safe. Useful for turning even, straight-line values into ones that feel more natural to the ear, or for gently softening the shape of a control signal.

Inputs: X (stream-capable; negative inputs are treated as 0). Outputs: Result (square root of X).
Exp
Calculates e^X (an exponential curve). Turns an even, straight-line control into a curved one, which makes sweeps and timing feel more natural to the ear.

Inputs: X (stream-capable). Outputs: Result (e raised to X).
Log
Calculates the natural logarithm, which does the opposite of Exp. It squeezes a very wide range of values down into a smaller, easier-to-handle range.

Inputs: X (stream-capable). Outputs: Result (natural logarithm of X).
Linear To dB
Converts a plain volume amount into decibels (dB, the usual way of measuring loudness): 20 * log10(amplitude). Useful for showing a level in dB or for feeding controls that expect dB. Values very close to zero are pinned to -144 dB.

Inputs: Linear (linear amplitude; 1.0 = 0 dB, 0.5 = -6 dB). Outputs: dB (the level in decibels).
dB To Linear
Converts decibels back into a plain volume amount: 10^(dB / 20). This is the reverse of Linear To dB, handy when you have a dB setting and need to turn it into an actual volume factor.

Inputs: dB (a level in decibels; 0 dB = 1.0, -6 dB = 0.5). Outputs: Linear (the linear amplitude).
Sin
Gives the sine of X, where X is measured in radians (a way of measuring angles). Use it to shape values into smooth, repeating waves, to drive oscillators by their phase, or to create gentle cyclic shapes.

Inputs: X in radians (stream-capable). Outputs: Result (sine of X).
Phase Sine
Like Sin, but it reads X as a phase position instead of an angle: 0 to 1 means one full cycle, and the output swings from -1 to 1. Feed it a repeating ramp (a phasor) and a smooth sine wave comes out, with no need to scale anything into radians first. Handy for phase-driven oscillators and cyclic shapes built from ramps.

Inputs: X (the phase, where 0 to 1 is one full cycle; stream-capable). Outputs: Result (the sine of the phase, -1 to 1).
Cos
Gives the cosine of X, where X is measured in radians (a way of measuring angles). Pair it with Sin to make two LFOs that move a quarter-step apart, or for smooth equal-volume crossfades.

Inputs: X in radians (stream-capable). Outputs: Result (cosine of X).
Tan
Gives the tangent of X, where X is measured in radians (a way of measuring angles). It makes steep, dramatic curves that shoot off toward infinity, good for bold, experimental sound-shaping.

Inputs: X in radians (stream-capable). Outputs: Result (tangent of X).
Floor
Rounds a number down to the nearest whole number. Use it to snap a smoothly changing value down onto whole-number steps.

Inputs: X (stream-capable). Outputs: Result (X rounded down).
Ceil
Rounds a number up to the nearest whole number. Use it to snap values upward, or to make sure a value always lands on or above a whole-number step.

Inputs: X (stream-capable). Outputs: Result (X rounded up).
Round
Rounds a number to the nearest whole number the normal way (down if it is below halfway, up if it is at or above). Use it to snap a control signal onto whole-number steps.

Inputs: X (stream-capable). Outputs: Result (X rounded to the nearest integer).
Greater Than
Compares two values and asks "is A bigger than B?". It sends out 1 when A > B, and 0 otherwise. Use it to react when a signal goes above a certain level, or to switch something on past a threshold.

Inputs: A, B (stream-capable). Outputs: Result (1 when A > B, otherwise 0).
Less Than
Compares two values and asks "is A smaller than B?". It sends out 1 when A < B, and 0 otherwise. This is the mirror image of Greater Than, for reacting when a signal drops below a level.

Inputs: A, B (stream-capable). Outputs: Result (1 when A < B, otherwise 0).
Equal
Checks whether two values match, with a little built-in wiggle room so near-matches still count. It sends out 1 when A and B are equal, and 0 otherwise. Use it to spot a particular value or to recognise a pattern.

Inputs: A, B (stream-capable). Outputs: Result (1 when A and B are equal, otherwise 0).
This compares numbers. Text arriving here reads as 0, so any two words come out equal: use Text Equal for that.
Text Equal
Checks whether two texts are the same and sends out 1 when they match, 0 when they do not, so a Branch or a Gate can act on it. The Equal node above cannot do this, because it compares numbers and text reaching it reads as 0, which makes any two words look equal. Type into A or B to test a wired input against a fixed word, or wire both. Numbers are compared as their text, so 5 matches "5". The match is exact, spaces included, since text that looks the same and is not is the thing this is here to catch; Match Case off ignores capitals only, so Cubase and cubase match but "Cubase " still does not. An event on any input re-answers without changing anything.
| Parameter | Range | Default | Description |
|---|---|---|---|
| A | text | empty | First text. Editable on the node. |
| B | text | empty | Second text. Editable on the node. |
| Match Case | on / off | on | Compare capitals as well. |
| Equal | Yes / No | - | Read-only. The pin sends 1 or 0. |
Inputs: A, B (text, or a number compared as its text; an event re-emits the answer), Match Case. Outputs: Equal (1 when the two match, otherwise 0).
Text Contains
New in MNodes 1.1.5
This node does not exist in older versions of MNodes. A project that uses it will not work there until you update.
Looks for one text inside another. Found sends 1 when Find is somewhere in Text and 0 when it is not, Position says where it first starts, counting from 0, or -1 when it is not there, and Count says how many times it appears. Position counts the same way as Sub Text's Start, so the two work together: find where a word is, then cut the text there. Found is sent after Position and Count, so whatever acts on Found already has the position of the same text. Index picks which time Found and Position answer for: 0 is the first, 1 the second, and a negative Index counts back from the end, so -1 is the last; Count is always every time. A Counter into Index walks them one by one. Type into Text or Find, or wire either one. With Match Case off, capitals are ignored. An empty Find finds nothing, and an event on any input answers again without changing anything.
Needs: a Text Contains node looking for "C" in "C4 E4 G4 C5", with Found, Position and Count showing.
| Parameter | Range | Default | Description |
|---|---|---|---|
| Text | text | empty | The text to look in. Editable on the node. |
| Find | text | empty | The text to look for. Editable on the node. |
| Match Case | on / off | on | Tell capitals apart. |
| Index | whole number | 0 | Which time Find appears to answer for: 0 the first, 1 the second, -1 the last. |
| Found | Yes / No | - | Read-only. The pin sends 1 or 0. |
| Position | whole number | - | Read-only. Where the match at Index starts, from 0, or -1. |
| Count | whole number | - | Read-only. How many times Find appears, without overlaps. |
Inputs: Text, Find (text, or a number searched as its text; an event re-emits the answer), Match Case, Index. Outputs: Found (1 or 0), Position (from 0, or -1), Count.
Clamp
Keeps X between a Min and a Max, leaving values that are already inside that range untouched. Useful for making sure a value never strays outside a safe range.

| Parameter | Range | Default | Description |
|---|---|---|---|
| X | Any value | 0 | The input value to clamp. |
| Min | Any value | 0 | Lower bound of the clamp range. |
| Max | Any value | 1 | Upper bound of the clamp range. |
Inputs: X, Min, Max (all stream-capable). Outputs: Result (X limited to the Min..Max range).
For Range
This node repeats a step over and over (a loop). Each time you trigger it, it counts from a Start number to an End number, sending out each number along the way plus a Done signal when it reaches the end. Use it to run loops, fill in lists, or play back scripted sequences. Step is always a positive amount; the direction (up or down) is worked out automatically from your Start and End numbers.

| Parameter | Range | Default | Description |
|---|---|---|---|
| Start | Whole numbers | 0 | The index the loop starts from. |
| End | Whole numbers | 7 | The index the loop runs to (inclusive). |
| Step | 1 and up | 1 | Step size between iterations (always positive; direction follows Start and End). |
Inputs: Trigger (any message starts the loop), Start, End, Step. Outputs: Index (the current loop index for each step), Event (one event per iteration), Done (fires once after the full range completes).
Once
A one-time pass. It lets the very first message through, then blocks everything after that until you reset it. Use it for things that should happen once and then stop.

| Parameter | Range | Default | Description |
|---|---|---|---|
| Armed | On / Off | On | Shows whether the node is ready to pass the next message. |
Inputs: In (passes only once while armed), Reset (any message re-arms the node). Outputs: Out (the first message, then nothing until reset).
Every
This thins out a stream of triggers. It counts the messages coming in and sends out one Event for every N of them. Use it to slow a busy stream of triggers down to a steadier pace. It keeps going forever; if you only want the very first trigger, add a Once node after the Event output.

| Parameter | Range | Default | Description |
|---|---|---|---|
| Steps | 1 and up | 1 | How many messages are needed before Event fires. Stream-capable, so the period can be modulated. |
Inputs: In (any message counts, including events), Steps, Reset (any message resets the count to 0). Outputs: Count (the current count), Event (fires every time the count reaches Steps).
Data Delay
Holds onto an incoming message for a set amount of time, then sends it on. Use it to space out triggers, line events up in time, or add a delayed reaction. The wait time is set in milliseconds and can be controlled by a CV signal. Several messages can be waiting their turn at the same time.

| Parameter | Range | Default | Description |
|---|---|---|---|
| Delay (ms) | 0 to 10000 | 100 | Time in milliseconds before the message is forwarded. Values below 1 ms pass through immediately. Stream-capable. |
Inputs: In (any message to delay), Delay, Cancel (any message flushes all waiting messages without sending them). Outputs: Out (the delayed message), Pending (how many messages are currently waiting).
This is a timer for messages, not an echo effect for sound. It is accurate to about 1 ms; if you want a delay on actual audio, use a dedicated audio delay node instead.
Probability
A chance-based gate. Each message that comes in has a set chance of getting through. Use it to add random variation to patterns, randomly drop some notes, or sprinkle in a bit of controlled randomness. If you connect a CV signal instead, it decides whether each individual moment of the signal passes.

| Parameter | Range | Default | Description |
|---|---|---|---|
| Chance | 0 to 100 % | 100 | The probability that each incoming message or stream sample passes through. |
Inputs: In (messages or a CV stream), Chance (stream-capable). Outputs: Out (the message or stream, only when the roll passes).
Gate
An on/off switch that works for both messages and CV signals. When it is open it lets things through, and when it is closed it blocks them. Use it to mute a path or to switch routing on and off. The Enable input accepts a CV signal, so you can open and close the gate rhythmically, even on audio.

| Parameter | Range | Default | Description |
|---|---|---|---|
| Enabled | On / Off | On | When on, In passes to Out; when off, Out is blocked (or silent for CV). Events on Enable are ignored. |
Inputs: In (message or CV, stream-capable), Enable (non-zero opens, zero closes; stream-capable). Outputs: Out (the forwarded message or CV when enabled).
Select
Watches an input value and fires an Event the moment it matches a value you choose. Use it to spot a specific state or to trigger something when a pattern reaches a certain point. Both inputs can be controlled at audio rate for precise timing.

| Parameter | Range | Default | Description |
|---|---|---|---|
| Target | Any value | 0 | The value X is compared against. |
Inputs: X (the value to check), Target (both stream-capable). Outputs: Match (an Event when X equals Target exactly).
Branch
A simple "yes or no" router. It looks at the incoming message and treats it as true or false: if it is true it fires the True output, otherwise it fires the False output. A quick way to send things down one of two paths. The node shows the last message it received.

Inputs: In (any message; numbers are true when non-zero, a bool passes through, text and events evaluate to false). Outputs: True (an Event when the input is true), False (an Event when the input is false).
Toggle
A simple on/off switch. Each trigger that comes in flips it to the opposite state and sends out the new value. Use it to build switches that stay set until flipped again.

| Parameter | Range | Default | Description |
|---|---|---|---|
| State | On / Off | Off | The current on/off value. Flip it with a trigger, or force it with the Set input. |
Inputs: Trigger (any message flips the state), Set (0/1 or false/true forces the state). Outputs: State (the current on/off value, sent on every change).
Latch
Remembers a value and only sends it out when you trigger it. Use it to grab a value and hold it, then pass it along whenever you want. Triggering it never changes the value it is holding; to change that, use the Set input. The held value is saved with your project.

Inputs: Set (stores the incoming value, does not output), Trigger (any message outputs the stored value). Outputs: Value (the stored value, emitted when triggered).
Counter
Counts the messages coming in, staying within a Min and a Max. Choose Up, Down, or Up/Down (bouncing back and forth) counting to drive step sequencers, count actions, or build pattern positions. The In trigger never sets the count directly; to set it, use the Set input. Its state is saved with your project.

| Parameter | Range | Default | Description |
|---|---|---|---|
| Mode | 0 = Up, 1 = Down, 2 = UpDown | Up | Counting direction. Stream-capable. |
| Min | Whole numbers | 0 | Lower bound of the counter range. Stream-capable. |
| Max | Whole numbers | 127 | Upper bound of the counter range. Stream-capable. |
| Set | Whole numbers | 0 | Forces the current count (no output). Stream-capable. |
| Wrap | On / Off | On | On, the count wraps round at the ends (Max to Min, Min to Max). Off, it stops at Min and Max. UpDown always bounces either way. |
Inputs: In (any message outputs the count then advances), Mode, Min, Max, Set, Wrap, Reset Min (any message sets the count to Min), Reset Max (any message sets the count to Max).
Outputs:
| Output | Description |
|---|---|
| Count | The current count, output on each trigger. |
| Min Hit | True when the current count equals Min. |
| Max Hit | True when the current count equals Max. |
Stepper
A manual up and down counter, like the little + and - spinner buttons on a form. Any message on Up adds 1, any message on Down subtracts 1, and the new count is sent out each time. The count stays between Min and Max: with Wrap on, stepping past Max jumps back to Min (and past Min back to Max); with Wrap off, the count simply stops at the edge. Use it to build your own spinners, value pickers, or anything that counts button presses. It is the hands-on sibling of Counter: Counter is the sequencer-style one, stepping through a pattern from a single trigger, while Stepper gives you separate Up and Down controls. The count is saved with your project.

| Parameter | Range | Default | Description |
|---|---|---|---|
| Wrap | On / Off | Off | What happens at the edges: on wraps around, off stops at Min or Max. |
| Min | Whole numbers | 0 | Lower bound of the count. |
| Max | Whole numbers | 127 | Upper bound of the count. |
| Set | Whole numbers | 0 | Forces the current count (no output). |
Inputs: Up (any message adds 1 and outputs the new count), Down (any message subtracts 1 and outputs the new count), Wrap, Min, Max, Set, Reset Min (any message jumps the count to Min and outputs it), Reset Max (any message jumps the count to Max and outputs it).
Outputs:
| Output | Description |
|---|---|
| Count | The current count, sent on each step or reset. |
| Min Hit | True when the current count equals Min. |
| Max Hit | True when the current count equals Max. |
OnChange
Only passes a value along when it is different from the one before it. This filters out repeated messages, saves the rest of your patch from needless work, and lets things react only when something genuinely changes. The first value after you create or reset the node always passes, and an event always passes.

Inputs: In (any message; duplicates of the previous value are suppressed), Reset (any message clears the history so the next value always passes). Outputs: Out (the value, only when it changed).
Is Number
Checks whether what came in is a real number, and passes the number along when it is. Made for checking things people type: it accepts a minus or plus sign and decimals (-5.67, +3, .5), and a single decimal comma also counts ("1,5" reads as 1.5), while text with extra characters is rejected. Valid answers on every input, but Value only fires when the input really is a number, so a control fed from Value keeps its last good value when something wrong is typed. Pair it with a Text Input widget to accept typed numbers safely. A value that is already a number passes straight through.

Inputs: In (text or a number to test). Outputs: Valid (true when the input is a valid number, false when not), Value (the parsed number, only when valid).
Is Hex
Checks whether the incoming text is a valid hex colour code. Codes with or without a leading # are accepted, in 3, 6 or 8 digit form (0af, ff8800, aaff8800). When the code is valid, Hex sends out a cleaned-up version (lowercase, no #, short 3-digit codes expanded to 6 digits) that the Colour node accepts directly, so someone can type a colour into a Text Input widget and recolour your patch with it. Valid answers on every input; Hex only fires when the code is valid.

Inputs: In (text to test as a hex colour). Outputs: Valid (true when the text is a valid hex colour), Hex (the cleaned hex code, only when valid).
Is Note
Checks whether the incoming text is a valid note name like C3, A#4 or Gb2, and turns it into the matching MIDI note number (C3 is note 60). Sharps and flats both work, and a plain number from 0 to 127 counts as a valid note too. Valid answers on every input; Note only fires when the input is a real note, so bad input never disturbs whatever comes after. Wire Note into Note To Freq to get a pitch in Hz, or pair the node with a Text Input widget so notes can be typed by name.

Inputs: In (text with a note name, or a note number to test). Outputs: Valid (true when the input is a valid note), Note (the MIDI note number, only when valid).
Integer to Bits
Takes a whole number and turns it into one on/off output per bit, the way a number is written in binary. Feed it 5 at a width of 8 and Bit 0 and Bit 2 come on while the rest stay off. Use it to drive several gates or switches from one number, to unpack a value someone sent you as a single number, or to read a number one bit at a time.

Width sets how many bits, and so how many outputs: 8, 16 or 24. The outputs appear and disappear as you change it, so a 24-bit node is a tall node. The ceiling is 24 on purpose: numbers travel around a patch as whole numbers with a sign, so a 32-bit width would turn the top bit into a minus sign and hand you negative results, and 24 is also the last width where every value stays exact even if something in the chain treats it as a decimal. For anything wider, divide the number with the maths nodes and use two of these.
Bit Order decides what each pin is worth, never what it is called. The pins are always Bit 0 upwards in both settings, so switching it re-reads the patch you already wired instead of asking you to move cables. LSB First (the default) means Bit 0 is the ones column and the last pin counts the most; MSB First is the opposite.
Numbers too big for the width are clamped, so at 8 bits anything above 255 arrives as 255 and negatives arrive as 0. Every bit is sent on every input, not only the ones that changed, so a node you wire up later gets the full picture straight away. Pairs exactly with Bits to Integer.
| Parameter | Type | Default | What it does |
|---|---|---|---|
| Integer | number | 0 | The number to split. Type it here or feed it through the pin. |
| Width | choice | 8 Bits | How many bits, and how many outputs: 8, 16 or 24. |
| Bit Order | choice | LSB First | Whether Bit 0 is the ones column or the largest one. |
Inputs: Integer (the number to split). Outputs: Bit 0 upwards (one on/off output per bit).
Bits to Integer
Takes one on/off input per bit and combines them into a whole number. Turn on Bit 0 and Bit 2 at a width of 8 and the output is 5. It is the exact opposite of Integer to Bits: wire one into the other and you get back what you put in.

Each input remembers its last value, so the number updates the moment any single bit changes rather than waiting for all of them, and an input that has never been fed counts as off. A number arriving on a bit input counts as on from 0.5 up, so a control signal and a plain on/off behave the same.
Width and Bit Order work exactly as they do on Integer to Bits, and the two nodes must agree on both for the round trip to come back the same. The Integer box is a readout rather than a control: the bits own the value.
| Parameter | Type | Default | What it does |
|---|---|---|---|
| Width | choice | 8 Bits | How many bits, and how many inputs: 8, 16 or 24. |
| Bit Order | choice | LSB First | Whether Bit 0 is the ones column or the largest one. |
Inputs: Bit 0 upwards (one on/off input per bit). Outputs: Integer (the combined number, sent whenever any bit changes).
Number Base
New in MNodes 1.1.5
This node does not exist in older versions of MNodes. A project that uses it will not work there until you update.
Shows the same number in binary, decimal or hex. Type or send text on In, say which base it is written in with From, and Text writes the number in the To base, while Value sends it on as a plain number. 01000001 in binary, 65 in decimal and 41 in hex are the same byte.
Needs: screenshot of the Number Base node on the graph, reading 01000001 from Binary into Hex.
Valid answers on every input and says whether the text really is a number in the From base, so with From set to Binary it tells you whether a text is made of 0s and 1s only. Value and Text only fire when it is, so whatever they feed keeps its last good value. A number arriving on In is read as a number whatever From says, so Bits to Integer can feed it directly.
Binary may start with 0b and hex with 0x or #, any base may start with a minus, and underscores between digits are fine (0100_0001). Spaces are not: two bytes with a space between them are two numbers, so split a line of bytes with Text Split first. Numbers go up to 32 bits. Hex comes out in capitals, without a prefix.
To light eight LEDs from one byte, feed Value into Integer to Bits.
| Parameter | Type | Default | What it does |
|---|---|---|---|
| In | text | empty | The text to read. Type it here or send it through the pin. |
| From | choice | Binary | The base In is written in: Binary, Decimal or Hex. |
| To | choice | Hex | The base Text writes the number in. |
| Digits | number | 0 | Pads Text with zeros to at least this many digits: 8 for a byte in binary, 2 in hex. |
Inputs: In (text or a number; an event answers again), From, To, Digits. Outputs: Valid (true when In is a number in the From base), Value (the number, only when valid), Text (the number written in the To base, only when valid).
Map
Takes a value from one range and rescales it into another. For example, turn 0..1 into MIDI notes, MIDI notes into Hz, a 0..127 knob into dB, or anything into anything else. With Clamp on, values outside the input range are pulled back in first; with Clamp off, the rescaling keeps going past the output range. All number pins can take a control signal. An event on X sends out the last mapped value again.

| Parameter | Range | Default | Description |
|---|---|---|---|
| In Min | Any value | 0 | Start of the source range. |
| In Max | Any value | 1 | End of the source range. |
| Out Min | Any value | 0 | Start of the destination range. |
| Out Max | Any value | 1 | End of the destination range. |
| Clamp | On / Off | On | When on, X is limited to the input range before mapping. |
Inputs: X (the value to remap), In Min, In Max, Out Min, Out Max, Clamp (all stream-capable). Outputs: Out (the remapped value).
Note To Freq
Turns a MIDI note number into a pitch in Hz (cycles per second), using 440 * 2^((note - 69) / 12). It can take a control signal, so a moving CV note coming in stays a moving CV pitch on the way out.

| Parameter | Range | Default | Description |
|---|---|---|---|
| Note | 0 to 127 | 60 | The MIDI note number. 60 is middle C, which is about 261.63 Hz. Stream-capable. |
Inputs: Note (MIDI note number). Outputs: Hz (the frequency in Hz; emitted as CV when the input is CV).
Pattern File
Stores a sequencer pattern (saved as JSON text), works out what kind it is, and lets you save it to disk or load it back. Connect a sequencer's Pattern Out to this node's Pattern In, and the node shows when it is ready, the kind of pattern it found, and an optional name read from the pattern. From there you can save it to a file or send it out again whenever you like.

| Parameter | Range | Default | Description |
|---|---|---|---|
| Pattern Name | Text | empty | An editable name stored with the pattern and written on export. Read from the pattern when available. |
| File | File path | empty | Pick a pattern file to load it immediately. |
The node also shows three read-only readouts: Pattern Type (the kind of pattern it found, which decides the file ending used when saving), Name (the pattern name plus the suggested file ending), and Status (Empty until a pattern is stored, then Ready). Export Dialog opens a save window, and Emit Pattern sends the stored pattern out again. The file endings used when saving are .mnriff for a MIDI Riff, .mnsliderseq for a Slider Sequencer, and .mntoggleseq for a Toggle Sequencer.
Inputs: Pattern In (Pattern JSON from a sequencer), Pattern Name (optional name), File (optional file path to load), Emit Pattern (re-sends the cached pattern). Outputs: Pattern Out (the loaded Pattern JSON, ready to reconnect into a sequencer's Pattern In).
Radio Link
Makes a handful of separate buttons behave as one group: turn one on and the others go off. The Radio Group widget already does this, but it draws its own segments, so they sit in a row, touching, all the same size and all wearing the same look. This node does the behaviour and none of the drawing, so the buttons can be ordinary Widget Toggles placed anywhere on the panel, at any size, each with its own picture.
One pair of cables per button:
- the button's State output goes to Button n on this node
- Button n on this node goes back to the button's Set input
Set rather than State In, because Set applies the value without announcing it again, so the ring never chases its own tail. State In works too, it is just chattier: the echo comes straight back, gets recognised and is dropped.
| Parameter | Range | Default | Description |
|---|---|---|---|
| Buttons | 2 - 16 | 4 | How many buttons are in the group. The pins follow. Lowering it drops the cables of the ones that go away |
| Mode | Single / Multi / Multi (min 1) | Single | Single keeps exactly one on, and clicking the one that is already on leaves it on, which is the radio contract. Multi allows any combination including none. Multi (min 1) allows any combination but never none |
| Index | 0 to Buttons-1 | 0 | The button that is on, counting from 0. In Multi it is the one that changed last. Writing to it turns to that button |
The buttons count from 0 as well, so Button 0 is the one Index reads as 0 and there is no arithmetic to do between a pin and the number that comes out of it.
Each button is a page. Button n says 1 for the button that is on and 0 for the others, every time the group moves, so wire it to what belongs to page n as well as to its button: an Appearance Changer setting Visible on the widgets of that page, or a Scene Group's Scale and Opacity, and the panel changes page. Index is the page number, and an input as well, so a preset, the host or another control can turn to a page without anybody pressing a button; a bare trigger into Select re-sends everything, which is how a patch shows the right page and lights the right button when it opens.
An event on a button input means "this one", so a plain Widget Button can join the group: it has no state to report, only a click, and a click is a perfectly good way to pick a page.
Inputs: Button 0..N-1 (each button's State output, or an event to pick it), Select (turn to a button by number, or a trigger to re-send). Outputs: Button 0..N-1 (what each button should be showing, sent whenever the group moves: wire it to its Set input and to what goes with it), Index (the button that is on), Changed (fires whenever the group actually moves).
Script
New in MNodes 1.1.5
This node does not exist in older versions of MNodes. A project that uses it will not work there until you update.
A small program in JavaScript between cables: what arrives on its inputs calls the code, and the code sends what it likes out of its outputs. Use it for the logic no node has, for text another node reads (a Scene, whose Script input takes a whole canvas from one cable, or its Zones or Set; a Shader's Code), for sequences, rules and counters that remember. It runs modern JavaScript in a sandbox: it reaches no files and no network, and a call that runs for more than a quarter of a second is stopped, so a loop that never ends cannot hold MNodes up. Each Script runs its code on a thread of its own, so a slow program holds back only itself: the sound, the other Data cables and the screen go on as they were. It can also play CV and MIDI, each change landing on the exact sample it asks for (see CV and MIDI out, below).

The code is written in the editor on the node. With Live on, it starts again a moment after you stop typing; Run (or Ctrl + Enter, Cmd + Enter on a Mac) does it at once. Examples has eleven to start from: Start, Counter, Scale, Zones Grid (knob zones for a Scene), Scene Set (zone values for a Scene), Scene in Code (a whole Scene written in the Script, its picture, zones and movement, sent over one cable into the Scene's Objects input), Scene Drawing (an interface the Script draws on a Scene with no GLSL: two knobs, a switch and a row of steps), Scene Controls (a synth panel in a few lines, with the ready-made controls), Scene Sound (bars, a ring and a mark sweeping round with a trail, on a Scene, following the sound on its In A with no code running), LFO and Random Melody. The one you pick opens over your code, to read and to copy from (select with the mouse or Ctrl + A, then Ctrl + C), and your code changes only if you press Replace; Ctrl + Z then brings yours back. When the code is one you wrote, Replace and New ask first. With Preview on, as it starts, the example also runs in place of your code while it is open, through the same outputs, so the Scene it draws on shows what it does; closing it starts your code again, with its state. Turn Preview off to only read. The cross or Esc closes the example. What the code prints, and its mistakes with the line they are on, show in the console under the code; Clear empties it, and the mouse wheel over it goes back through what it said. A mistake while it runs leaves the rest of the code running for the next message. New starts again from the smallest program there is (Ctrl + Z brings back what you had), and Reference lays a sheet over the code with everything the code can use (the node's calls, drawing on a Scene, changing what is drawn, 3D and the controls), each with a button that writes it where the cursor is. Edit Outside opens the code in another editor, such as VS Code (see Writing the code in another editor, below). The whole node is the code's while you work in it: a click anywhere on it gives the code the keys, so Ctrl + A, C, V, X, Z and Y (Cmd on a Mac) act on the text, never on the graph, and Ctrl + mouse wheel makes the text bigger or smaller, the console's too.
What the code can define, and use:
| Name | What it is |
|---|---|
| onInput(n, value) | Called for every message on In n (In 1 is 1): value is a number, text, true or false, or undefined for an Event. |
| onTick(dt) | Called Tick times a second, when Tick is on: dt is the seconds since the last. With the picture, once each time the screen refreshes. |
| onStart() | Called when the code starts, and again after every edit or Restart. |
| out(n, value) | Sends value out of Out n: a number, text, true or false as they are, an array or an object as JSON text. |
| out(n) | If out's second parameter is empty, it sends an Event (a trigger) out of Out n: if (value === 3) out(1); fires whatever is wired to Out 1, with no Equal or Event node in between. |
| print(...) | Writes a line in the console; objects are written as JSON. |
| state | An object kept with the project, and across edits: what the code puts in it is still there when the project opens. |
| time() | The seconds since the code started. |
| canvas(n) | The Scene that Out n is wired into, to draw on: see Drawing on a Scene, below. |
| cv(n, value, { at, glide }) | Moves CV n to value, now or at the time at, at once or gliding over glide seconds. CV Outputs says how many there are. |
| note(n, pitch, velocity, length, { at, channel }) | Plays a note out of MIDI n: pitch 60 is middle C, velocity 1 to 127, length in seconds. MIDI Outputs says how many there are. |
| noteOn, noteOff, cc, bend, allNotesOff | The rest of MIDI: a note held until its off, a controller (0 to 127), pitch bend (-1 to 1), and every note off. Each takes { at, channel } too. |
| now() | The clock at is on, in seconds. |
| transport() | The tempo and where the song is: playing, bpm, beat, beatInBar, beatsPerBar. |
| atBeat(beat) | The time a beat falls at, for at. |
| onKey(name, down) | Called for every key of the computer keyboard pressed (down is true) and let go (false): name is "Left", "Space", "A" and so on. See Keys, below. |
| keyDown(name) | Whether that key is down now. |
| gamepad(n) | What game controller n (1 to 4, 1 if not said) is doing now: connected, the buttons A, B, X, Y, LB, RB, Back, Start, LS, RS, Up, Down, Left, Right (true while down), the sticks lx, ly, rx, ry (-1 to 1, up and right positive) and the triggers lt, rt (0 to 1). See Keys, below. |
| app.quit(), app.fullScreen(on) | In the standalone app, for a game's menu: quit leaves MNodes (asking to save first); fullScreen(true) fills the screen, fullScreen(false) gives it back, fullScreen() turns it the other way round; app.isFullScreen() says which. Never in the code's first moment and a half, and nothing in a DAW. See the App node. |
| onButton(name, down, pad) | Called for every button of a game controller pressed (down is true) and let go, with the pad's number. |
| Parameter | Range | Default | Description |
|---|---|---|---|
| Restart | button | Starts the code again: onStart runs, and the state stays. | |
| File | path | A .js or text file the code is taken from, every time it is saved: see Writing the code in another editor, below. | |
| Inputs | 1 to 8 | 2 | In the Inspector. How many inputs the node has. |
| Outputs | 1 to 8 | 2 | In the Inspector. How many outputs. |
| CV Outputs | 0 to 8 | 0 | In the Inspector. How many CV outputs the code moves with cv(). |
| MIDI Outputs | 0 to 4 | 0 | In the Inspector. How many MIDI outputs the code plays with note() and the rest. |
| Tick | Off / 10 / 30 / 60 / 120 a second / With the picture | Off | In the Inspector. How often onTick runs, if the code has it. With the picture runs it once each time the screen refreshes, so what the code moves on a Scene goes forward by the same amount in every picture: the one to choose for a game. With the window closed it goes on at the same rate. New in MNodes 1.1.6. |
| Code | text | Start | The program. It can also be edited in the Inspector. |
| Live | on / off | on | Starts the code again as you type, once it has no mistake to stop at. |
| Text Size | 9 to 28 | 13 | How big the code and the console are drawn; Ctrl + mouse wheel over the node does the same. |
Inputs: In 1 to In 8 (as many as Inputs says), each calling onInput; Restart, which starts the code again on any message; File, the path of a code file to follow, where an Event reads the file again at once.
Outputs: Out 1 to Out 8 (as many as Outputs says): what the code sends with out(). Wired into a Scene's Objects input, Out n is also where canvas(n) draws. CV 1 to CV 8 and MIDI 1 to MIDI 4 (as many as CV Outputs and MIDI Outputs say): what cv() and note() play.
Writing the code in another editor
New in MNodes 1.1.6
Older versions of MNodes do not have File and Edit Outside.
The editor on the node is made for short programs. For a longer one, write it in a code editor such as VS Code, and the Script follows it:
- Edit Outside, on the node's toolbar, asks where to keep the code (next to the project, unless you pick another place), writes it there as a .js file and opens it in your code editor: VS Code, Cursor or VSCodium if you have one, else Notepad++ or Sublime Text, else Notepad on Windows or TextEdit on a Mac. Once the file is linked, Edit Outside opens it again.
- Or give File a .js or text file you already have: drop it on File, pick it with the folder button, or type its full path.
From then on, every time you save the file, the Script reads it and starts again with it, whatever Live says, and its state stays; a mistake shows in the console as usual. The line under the code says which file the Script follows. While it follows one, the editor on the node shows the file and cannot change it, New does nothing, and the buttons on the Examples and Reference sheets copy what they would write, to paste in your editor. Clear File to write on the node again: the code stays as the file left it.
The code is saved in the project too, so the project opens the same where the file is not, and Collect All & Export puts the file in the bundle. On the File input, a path links a file and an empty one stops following.
CV and MIDI out
New in MNodes 1.1.6
Older versions of MNodes do not have CV Outputs and MIDI Outputs.
Set CV Outputs or MIDI Outputs in the Inspector and the Script gets those outputs: a CV it moves, and MIDI it plays, into anything that takes CV or MIDI, a filter's cutoff, an envelope's gate, a Poly Module. A CV holds the last value it was given; with glide it slides there over that many seconds instead of jumping.
Every one of them can take a time: { at: t }, on the clock now() reads, in seconds. The change then lands on that exact sample, however busy the code's thread is, so the way to play in perfect time is to schedule a little ahead. With Tick at 30 a second, this plays a note on every beat and moves CV 1 to a new height with it:
js
let next = 0; // the next beat to play
function onTick() {
const t = transport();
if (!t.playing) return;
if (next < t.beat) next = Math.ceil(t.beat);
// what falls in the next tenth of a second, scheduled now
while (atBeat(next) < now() + 0.1) {
const at = atBeat(next);
note(1, 60 + (next % 4) * 3, 100, 0.2, { at: at });
cv(1, Math.random(), { at: at, glide: 0.1 });
next += 1;
}
}Without a time, a change plays at the start of the next block, a few milliseconds on. Scheduling ahead also keeps a sequence steady while the code does something long: a call that builds a big interface again holds back the next tick, but never what was already scheduled. For a sequence that has to stay perfectly even beside a big interface, give each its own Script. A Restart (or an edit) holds things back the longest: the code runs again from the top, and what comes in meanwhile waits until it has, about a tenth of a second with a big interface. So a Script that plays a sequence should not restart itself to change its look (a light or dark theme, say): keep the sequence in a Script that nothing restarts. A note never sounds twice at once on the same output: a second note on for a note that is already sounding ends it first. When the code starts again (an edit, Restart), what it had scheduled is forgotten and its notes end; when the node is bypassed its notes end and its CV outputs hold. Inside a Poly Module the CV and MIDI outputs play once, from the first voice: the code is one for all the voices. A mistake in using them (an output the node does not have, a value that is not a number) is said once in the console.
Keys, for a game
New in MNodes 1.1.6
Older versions of MNodes do not have onKey and keyDown.
A Script hears the computer keyboard by itself, with no Keyboard node: write onKey(name, down) and it runs for every key pressed and let go, or ask keyDown(name) whether a key is down right now. Names are the ones the Keyboard node gives: "Left", "Right", "Up", "Down", "Space", "Enter", "Escape", letters as "A" to "Z", digits as "0" to "9". With Tick on, this moves a dot on a Scene with the arrows, as smoothly as the Tick runs:
js
const c = canvas(1);
c.size(480, 300);
const dot = c.circle({ x: 240, y: 150, r: 12, fill: "#4fd1ff" });
let x = 240, y = 150;
function onTick(dt) {
const speed = 200 * dt;
if (keyDown("Left")) x -= speed;
if (keyDown("Right")) x += speed;
if (keyDown("Up")) y -= speed;
if (keyDown("Down")) y += speed;
dot.set({ x: x, y: y });
}
function onKey(name, down) {
if (name === "Space" && down) dot.animate({ r: 20 }, { duration: 0.15, yoyo: true, repeat: 1 });
}A game controller works the same way: gamepad(1) says what pad 1 is doing now (its buttons by name, its sticks lx, ly, rx, ry from -1 to 1 and its triggers lt, rt from 0 to 1, still sticks at 0), and onButton(name, down, pad) runs for every press and release. In the example above, x += 200 * dt * gamepad().lx moves the dot with the left stick. On Windows any pad that speaks XInput works (Xbox pads and most others), on a Mac those the system knows (Xbox, PlayStation, MFi); the pads are read only while a Script uses them.
Keys reach the Script while the MNodes window has the keyboard, and not while you type in a text box, the Script's own editor included, so writing code never moves your game. A Script whose code has neither onKey nor keyDown does not take the keys. Shortcuts keep working: a key that is also a shortcut, such as B for bypass with a node selected, does both, so play a game in Perform, where nothing is selected.
Drawing on a Scene
canvas(n), or scene(n), which is the same, draws on the Scene that Out n is wired into, through the Scene's Objects input. The code makes shapes, text and pictures, changes them, moves them over time and hears what the mouse does to them, with no GLSL at all. The canvas's own picture can stay underneath, or give way to a plain background.
js
const c = canvas(1); // the canvas wired to Out 1
c.size(480, 300); // lay things out on 480 x 300, fitted to the canvas
c.background("#101018"); // a plain background: the canvas's own picture is hidden
const dot = c.circle({ x: 240, y: 150, r: 40, fill: "#223", stroke: "#4fd1ff", strokeWidth: 3 });
dot.on("drag", e => dot.set({ x: dot.get("x") + e.dx, y: dot.get("y") + e.dy }));
dot.on("click", () => dot.animate({ r: 60 }, { duration: 0.4, ease: "outBack" }));Places count from the canvas's top left corner, in the units c.size() sets (the canvas's own size when the code sets none), and the whole scene is scaled to fit whatever size the canvas has.
A place or a size can also be a share of what it is in, written as a text: c.rect({ x: "10%", y: "100% - 50", w: "80%", h: 40 }) stays across the foot of the canvas whatever its size. "50%" is half the width for a place across or a width, half the height for a place down, a height or a text's size, and half the smaller side for a radius; "50%w", "50%h", "50%min" and "50%max" name the side, and terms add and take away ("100% - 20", "calc(50%w + 10)"). They count from the canvas (the units c.size() sets, or its own), from a group given a w and an h (what it holds counts from its box), or, for a label, from its 3D view. A binding or a change over time can go to a share too: bind: { x: ["p1", "0%", "100%"] }. Sizes in a 3D world are the world's own units.
Each maker takes the object's properties and gives the object back, to keep in a variable:
| Maker | What it draws | Its own properties |
|---|---|---|
| c.rect({...}) | A rectangle | x, y (its top left), w, h, radius (round corners) |
| c.circle({...}) | A circle, or an ellipse | x, y (its middle), r, or rx and ry |
| c.arc({...}) | Part of a ring, as a knob shows its value | x, y (its middle), r, width, start and end (in degrees, 0 at the top, going clockwise), cap |
| c.line({...}) | A straight line | x1, y1, x2, y2, width, cap (round, butt or square) |
| c.polyline({...}) | Lines through points | points ([[x, y], ...] or [x, y, x, y ...]), closed, width, cap |
| c.triangle({...}) | A triangle | x1, y1, x2, y2, x3, y3 |
| c.text({...}) | Text, one line or several (\n) | x, y, text, size, align (left, center, right), baseline (top, middle, bottom), lineHeight |
| c.image({...}) | A picture from a file | x, y, w, h, src (the file's whole path, or image1 or image2 for the canvas's own Image 1 and Image 2), fit (contain or cover), radius |
| c.polygon({...}) | A shape through points, filled | points ([[x, y], ...] or [x, y, x, y ...]), counted from its x and y |
| c.path({...}) | Any outline, written as SVG writes a path | d, such as "M 0 0 L 100 0 Q 150 50 100 100 Z" (see below), or points and closed; counted from its x and y |
| c.star({...}) | A star, its first point at the top | x, y (its middle), r, sides (how many points), inner (how far its inner corners are, a share of r) |
| c.ngon({...}) | A regular polygon, its first corner at the top | x, y (its middle), r, sides (6 unless you say) |
| c.wave({...}) | The sound on the Scene's In A or In B, as it plays | x, y, w, h, input ("a" or "b"), shows ("wave" or "spectrum"), style (line, fill, bars or mirror), bars (how many), gain, log (false spreads a spectrum's frequencies evenly instead of by octaves) |
| c.group({...}) | A group of objects | x, y: what it holds counts from there; w, h: a box its shares count from; clip: [x, y, w, h], or [x, y, w, h, radius] for round corners, cuts what it holds to that box (true: its own box, round by its radius); reveal: 0 to 1, shows only so much of its box from one side, revealFrom "left" ("right", "bottom", "top"), for a fill or a meter made of any drawing |
A path's d is written as SVG and drawing programs write it: M x y moves the pen there, L x y draws a straight line to there, H x and V y go straight across and down, C draws a curve through two control points (x1 y1 x2 y2 x y), S a curve that carries on smoothly from the last one, Q a curve with one control point, T one that carries on from it, A an arc (rx ry turn large sweep x y), and Z closes the outline back to where it started. A small letter counts from the last place instead of from the top left. A path with several outlines leaves the inside of an inner one empty, so an "O" has its hole. A wave reads the sound on the graphics card, so it is drawn on every frame however much it moves, and costs about what a rectangle does.
In a group, g.rect({...}) and the other makers put the new object inside it. Every object also takes:
| Property | What it does |
|---|---|
| fill | Its colour: "#4fd1ff", "#4fd1ff80" (the last two digits its alpha), "rgb(79, 209, 255)", "rgba(79, 209, 255, 0.5)", a colour's name, or a gradient: { from: "#000", to: "#fff", angle: 90 } (0 goes left to right, 90 top to bottom), { from: "#000", to: "#fff", radial: true } from the middle out, or { from: "#000", to: "#fff", conic: true, angle: -135, span: 270 } round the middle, starting at angle (in degrees, 0 at the top, going clockwise) and reaching to after span degrees (360 when there is none). A picture's fill tints it. |
| fill with a pattern | { from: "#101820", to: "#4fd1ff", pattern: "stripes", size: 8, weight: 0.5, angle: 45 }: the pattern in to, over from. The patterns are stripes, dots, checker, grid and noise; size is how wide each cell is, in units, weight how much of a cell the pattern takes (a stripe's width, a dot's, a grid line's, 0 to 1), and angle turns it. patternX and patternY move it, so animate or bind them to make it slide. |
| stroke, strokeWidth | Its outline, and how thick. A line and an arc are drawn in their stroke. |
| glow, glowColor | A soft light round it, so many units wide, in that colour. |
| shadow | A shadow under it: a colour ("#00000080"), or { color: "#00000080", x: 0, y: 4, blur: 12 } to move it (in units, the same way down the canvas however the object is turned) and soften it. false takes it away. An outline alone casts a ring. |
| blur | Softens its edges over so many units. On text, blur, shadow and glow soften over a short distance only, about a fifth of the letters' size. |
| trail | Where it has been stays behind it and fades to a tenth over so many seconds, so a moving object draws a streak. On a group, everything in it leaves its trail. A canvas keeps 8 trails at most; the canvas's Reset empties them. |
| opacity | From 0 (not seen) to 1. A group's goes for everything in it. |
| rotation, scale | Turned (in degrees, clockwise) and sized round its middle; originX and originY move that point (0 to 1 across the object). |
| visible | false hides it, and what it holds. |
| blend | "add" adds its light to what is under it. |
| cursor | The pointer over it: pointer, grab, crosshair, text, move, ns-resize, ew-resize or none. |
| tooltip | A text shown when the pointer rests on it. |
| interactive | false lets the mouse through it; true makes it take clicks even with no handler. |
| id | A name to find it again with c.find(id). One is made when you give none. |
Changing what is there:
| Call | What it does |
|---|---|
| o.set({ x: 100, fill: "red" }) | Changes it at once. |
| o.animate({ x: 300, fill: "#fff" }, { duration: 0.5, ease: "outBack", delay: 0, repeat: 0, yoyo: false }, then) | Changes it over time: the canvas moves it on every frame, however often the code runs. A number in place of the options is the duration. Numbers and colours move; anything else is set at once. then(), if you give it, runs when the change is over. |
| o.stop() | Stops its changes over time where they are. |
| o.get("x") | The last value given to it. |
| o.front(), o.back() | Over, or under, the objects next to it. |
| o.remove() | Takes it away, with everything in it. |
| g.clear(), g.redraw(fn) | Empties a group; or empties it and lets fn(g) fill it again, to draw a layer anew on every tick. |
| c.clear() | Takes away everything the Script drew. |
| c.find(id) | The object with that id. |
The easings: linear, inQuad, outQuad, inOutQuad, inCubic, outCubic (the default), inOutCubic, inSine, outSine, inOutSine, inExpo, outExpo, inOutExpo, inBack, outBack, inOutBack, outElastic and outBounce.
The mouse: o.on(event, fn) calls fn when the mouse does something to the object, or to anything in it for a group: enter, leave, move, press, release, click, doubleclick, dragstart, drag, dragend, wheel, rightclick, and animationend when a change over time is over. fn is given what happened: e.x and e.y (where, in the scene's units), e.u and e.v (where on the object, 0 to 1 across it), e.dx and e.dy (how far a drag moved since the last one), e.totalX and e.totalY (how far since the press), e.delta (the wheel), e.shift, e.ctrl and e.alt, and e.target (the object). It goes to the object, then to the groups it is in, then to c.on(...), until one of them calls e.stop(). An object that listens takes the press, so the canvas's zones under it get nothing; one that only listens to enter and leave lets the press through.
The canvas: c.size(width, height, fit) sets the units the scene is laid out in (fit is contain, which keeps its shape, stretch, which fills the canvas, or cover, which fills it and cuts what goes past). c.background(colour) gives the canvas a plain background and hides its own picture, and its zones, which then leave the mouse alone; c.showCode(true) brings them back under the scene.
What a Script draws is its own: it is drawn again whenever the code starts, it is not saved with the canvas, and it goes when the cable does. The canvas's own picture, zones and inputs work as they always do, under it. A mistake in what the code draws (a property that does not exist, a colour that is not one) shows in the console once, and the rest is drawn.
In three dimensions
c.view3d({...}) puts a 3D view on the canvas: a box (x, y, w, h, as a rectangle's, with its radius, outline, glow and shadow) with a world inside it, drawn by the graphics card with light as metals and plastics catch it, smoothed edges and fog. What you make inside it is in that world: v.box(), v.sphere(), v.cylinder(), v.cone(), v.torus(), v.plane(), v.mesh() for a model from an OBJ or glTF file (src, and for a glTF one with animations animation, its name, and animationTime, in seconds: animate animationTime to play it), v.light(), v.sound({ name }) for where a sound is (a Scene 3D Sound with that name as its Script Sound plays from there) and v.listener() for where it is heard from (a Scene 3D Listener with Script Room set to "room" hears it), v.zone({ w, h, d, roomMix, roomSize, roomDecay }) for a place that sounds like a room (that listener gives what it hears its reverb), v.group() to move and turn several together, v.clones() to copy what it holds many times, and v.particles().
js
const c = canvas(1);
const v = c.view3d({ x: 20, y: 20, w: 440, h: 260, fill: "#101820", orbit: true,
environment: "studio" }); // lit by a studio, which shines in the metal
v.plane({ w: 8, d: 8, y: -0.5, texture: "image1", tile: 4 }); // the canvas's Image 1, 4 times each way
const ring = v.torus({ r: 0.8, tube: 0.25, fill: "#e8b64a", metal: 1, rough: 0.3,
bind: { rotY: ["time", 0, 360, 0.2] } }); // turns by itself
v.sphere({ r: 0.4, segments: 6, flat: true, fill: "#4fd1ff", // a gem
bind: { scale: ["bass", 0.8, 1.4] } }); // swells with the bass
v.light({ type: "spot", x: 0, y: 4, z: 2, cone: 25, intensity: 20 }); // aimed at the middleIn the world y is up, x across and z towards you, in its own units: a box 1 across seen from 5 away fills about a fifth of the view.
| Property | On | What it does |
|---|---|---|
| x, y, z | solids, lights, groups | Where it is. A group's contents count from it. A light with no place of its own is at x -2, y 4, z 3, up and to the left, as a sun; a point or spot light made inside a group starts at the group's middle instead, so a lamp in a group is where the group is. |
| rotX, rotY, rotZ | solids, groups | Turned round each axis, in degrees (rotation is rotZ). |
| scale, scaleX, scaleY, scaleZ | solids, groups | Its size: 2 twice as big. |
| w, h, d | box, plane, mesh | Its width, height and depth. A mesh is fitted into a box 1 across first. |
| r, h | sphere, cylinder, cone | Its radius, and a cylinder's or a cone's height. |
| r, tube | torus | Its ring's radius, and its tube's. |
| src | mesh | The OBJ file's whole path. |
| fill, opacity | solids | Its colour; under 1, what is behind it shows through. |
| metal, rough | solids | 0 a plastic, 1 a metal; 0 polished, 1 matt. |
| glow, glowColor | solids | The light it gives of its own, for a lamp or a screen: glow times its colour, so over 1 it is brighter than white. |
| flat | solids | true shades each face as one. |
| segments | sphere, cylinder, cone, torus | Its sides round: 0 smooth. A few, with flat: a gem, a prism, a pyramid (a cone of 4). |
| texture, normalMap | solids | A picture on it, and a normal map's bumps that catch the light: a file's whole path, "image1" or "image2" for the canvas's own pictures, or a pattern made here by its name (pictures: "checker", "grid", "stripes", "dots", "bricks", "tiles", "wood", "marble", "noise"; bumps: "bricks", "tiles", "noise", "waves", "dimples", "brushed"). A solid one's clear parts are cut away. |
| tile, tileU, tileV, offsetU, offsetV | solids | How many times its pictures repeat across it (tile for both ways), and how far they are moved: animate offsetU to slide them along. |
| bump | solids | How strong the normal map's bumps are: 0 none, 1 as drawn. |
| mirror | plane | How much it reflects the world as a mirror does (1: a perfect mirror), blurred as it is rough, rippled by its normal map; what is under it is not seen in it. A view takes 4. |
| type, intensity, fill, r | light | "directional" (as the sun, from x, y, z towards the middle), "point" (a lamp at x, y, z, reaching r away), "spot" (a stage light at x, y, z), "panel" (a glowing rectangle w by h at x, y, z, facing tx, ty, tz) or "environment" (the light from all round: its environment, envYaw and showEnvironment, as a view's; with environment "" an even light of its fill); how bright; its colour. A view takes 32 lights; the first that cast shadows, as many as its shadows have room for, cast them. |
| tx, ty, tz, cone, soft | light | Where a spot points (in its group's units), how wide its light is from its middle to its edge (in degrees), and how much of that fades out (0 a sharp circle). |
| shadows, blur | light | Every light casts shadows unless shadows is false, a point all round it, as soft as blur says (texels of its shadows' picture; 1.5 unless said). A view keeps 16 views of them: from far away or a spot one, a point six. A see-through solid casts a lighter shadow, a picture's clear parts none. |
| yaw, pitch, distance | view3d | Its camera: turned round what it looks at, raised above it (in degrees), and how far from it. |
| tx, ty, tz, fov | view3d | What the camera looks at, and how much it sees, top to bottom, in degrees. |
| ortho | view3d | true sees it straight on, with no perspective, as in a plan. |
| fill, ambient | view3d | Its background (none shows the canvas behind), and the light from all round when it has no surroundings, more from above than below. |
| environment | view3d | Its surroundings: "studio", "sky", "sunset", "night", or a panorama's whole path (an .hdr, or any picture twice as wide as it is tall). Their light falls on everything, and what is smooth shines with them, sharp or blurred as it is rough. |
| showEnvironment, intensity, envYaw | view3d | true shows the surroundings behind the world; how bright their light is; how far they are turned round, in degrees. |
| exposure | view3d | How much light the camera takes in: 2 twice as bright. |
| bloom, bloomThreshold, bloomSize | view3d | How strongly what is brighter than bloomThreshold (1: white) glows round it, and how far the glow spreads (0 a tight halo, 1 a wide haze). |
| focus, focusDistance, focusRange, tiltShift | view3d | Depth of field: how blurred what is out of focus is (0 to 1), how far from the camera it is sharp (0: where the camera looks), how deep the sharp part is; tiltShift 1 makes it a band across the middle of the picture instead, as a photo of a miniature (focusRange then its height). |
| ao, aoRadius | view3d | Ambient occlusion: corners, creases and contact with the ground darker (0 to 1), looking aoRadius units round each point. |
| saturation, contrast, warmth, vignette | view3d | The picture graded: colour strength (1 as it is), contrast (1 as it is), warmth (-1 cool to 1 warm), corners darker (0 to 1). |
| edges, edgeWidth, edgeColor | view3d | Outlines where what is drawn ends or turns sharply (0 to 1), how wide in pixels, their colour. |
| fog, fogColor | view3d | How thick the air is (0 for none), and what the distance fades into. |
| orbit | view3d | true lets the mouse turn the camera round (drag), slide what it looks at (Shift and drag) and bring it nearer (the wheel). |
| count, layout, spacing, columns, r, turns | clones | How many copies of what it holds, laid out as "line", "grid", "circle", "spiral", "sphere" or "random": spacing apart in a line or a grid (columns across), r out on a circle, a spiral or a sphere (turns times round), anywhere in a box 2r across at random. |
| turnEach, riseEach, growEach, jitter, seed | clones | Each copy turned (degrees), raised and grown this much more than the one before; moved, turned and sized at random by jitter (0 to 1), as seed says. |
| spectrum, input | clones | Each copy as tall as its band of the spectrum of In A ("A") or In B ("B"), spectrum times at full level; what it holds grows from its foot at 0. |
| rate, life, speed, spread, gravity, drag | particles | How many a second, how long each lives, how fast they leave along its up within spread degrees, and how they are pulled down and slowed. p.burst(n) makes n at once. |
| size, sizeEnd, fill, colorEnd, glow, blend, shape, count | particles | Each one's size and colour when made and at the end of its life (no colorEnd: its colour fading out), the light it gives, "add" (the default) or "normal", "sphere" or "box", and how many can be alive at once. |
Anything flat made in a 3D world (a text, a circle, a control) is a label: it is drawn over the view where the middle of its 3D group is seen (the world's middle if it is in none), following it as the camera turns, its own x and y moved from there in canvas units, and hidden when that point is behind the camera. v.group({ y: 1.2 }).text({ text: 'Ring', align: 'center' }) names what is under it; in clones, each copy has one. A group with label: true is flat as a whole, one label with everything in it (as the canvas's controls are). A view with hideLabels: true hides a label while a solid stands between it and the camera.
Everything moves as on the flat canvas: animate(), and bind to the sound, the P controls, the time or a zone, so a world can turn and pulse with no code running. A solid hears the mouse too: sphere.on("click", ...) is told when the pointer is on it, found by a ray from the camera.
Physics
A solid or a 3D group with body becomes a body that falls, collides, bounces and comes to rest in its view's world: body: "dynamic" falls and is pushed, "static" stays where it is put (the ground, a wall), "kinematic" goes where its x, y and z say (set them, or animate them), pushing what is in its way. A group is one body of all it holds. Its x, y, z and turns are then the world's: set them to put it somewhere else, and it carries on from there.
js
const c = canvas(1);
const v = c.view3d({ x: 0, y: 0, w: 480, h: 300, environment: "sky", yaw: 30, pitch: 25, distance: 14 });
v.light({ type: "directional", x: -4, y: 8, z: 3, shadows: true });
v.plane({ w: 20, d: 20, fill: "#7fb069", body: "static" });
for (let i = 0; i < 8; ++i)
v.sphere({ x: i - 3.5, y: 4 + i, r: 0.4, fill: "#ffd166", body: "dynamic", bounce: 0.6 })
.on("hit", e => { if (e.strength > 0.1) note(1, 60 + 2 * i, Math.round(127 * e.strength), 0.3); });| Property | On | What it does |
|---|---|---|
| body | a solid, a 3D group | "dynamic", "static" or "kinematic" (none: no body). |
| mass | a body | How heavy it is, in kilograms (0, the default: from its size, as water). |
| bounce, friction | a body | 0 a thud to 1 a superball; 0 ice to 1 rubber (0.5 unless said). |
| damping, gravity | a body | How much the air slows it (0.05); how much gravity pulls it (1; 0 floats, below 0 rises). |
| sensor | a body | true: it stops nothing and only feels what comes in and goes out (bodyenter, bodyexit). |
| upright | a body | true: it never turns over (a character). |
| fast | a body | true: thrown very fast, it does not pass through thin walls. |
| collider | a solid body | Its shape round it in place of its own: "box", "ball", "capsule" (a character), "cylinder". A model is otherwise the box it is fitted into. |
| gravity, speed, paused | physics | v.physics({...}) sets its view's: how hard everything is pulled down (9.81), how fast its time goes (1), stopped or not. |
| name | a body | Its name, for joints of other owners (a node's) to find it by. |
| joint, a, b | joint | v.joint({...}) joins body a to body b (an Item, an id, or a body's name), or with no b to the world where the joint is: "hinge" (the default), "spring", "rope", "ball", "fixed" or "slider". The two do not collide with each other. |
| x, y, z, axis | joint | Where it holds them, and which way a hinge turns round or a slider slides: "x", "y" or "z" (the default, swinging in the plane facing you), tilted with rotX, rotY, rotation. |
| min, max | joint | A hinge's limits in degrees (min 0 to -180, max 0 to 180; its angle is 0 with body a turned as the joint is), a slider's in units; 0 and 0, none. |
| speed, strength, friction | joint | A motor: degrees a second round a hinge, units a second along a slider; the most it pushes with (0: as much as it takes; with no speed, a brake); how stiffly it turns or slides. |
| stiffness, damping, length | joint | A spring's bounce, times a second (2; 0: a rigid rod), how soon it settles (0.1), its length at rest; a rope's longest (no length: as far apart as when made). |
A body's events, with on(): hit each time it begins to touch something (e.other is what it hit, the Item when it is this Script's, and e.otherId its id; e.strength how hard, 0 to 1; e.speed how fast they met, in units a second; e.point and e.normal where, and which way from it); bodyenter and bodyexit when a body comes into or leaves a sensor (both are told); bodymove every frame it moves (e.point where it is, e.speed how fast); and a joint's jointmove whenever it moves (e.value a hinge's angle in degrees, a slider's place, a spring's or a rope's length; e.speed how fast that changes). A joint changes as it goes: wheel.set({ speed: 180 }).
js
// A pendulum: a ball held 3 from a pin, swinging, its angle out of CV 1.
const bob = v.sphere({ x: 3, y: 6, scale: 0.5, fill: "#ffd166", body: "dynamic" });
v.joint({ joint: "hinge", a: bob, x: 0, y: 6, axis: "z" })
.on("jointmove", e => cv(1, e.value / 90));Its methods: push(x, y, z) adds to its speed (or push({ x, y, z, spinX, spinY, spinZ }), the spins in degrees a second), velocity(x, y, z) sets it, body() says where it is and how fast it goes now ({ x, y, z, vx, vy, vz, awake }, in its view's world), restart() puts every dynamic body of its view back where it was put, and a view's ray(from, to) finds the first body a straight line meets ({ item, id, x, y, z, normal, distance }, or null). Hidden (visible: false), a body is in no world; one with opacity 0 is unseen but still solid, an invisible wall. The worlds go on with the plug-in's window closed, or the Scene on no screen: their hits are still told to the code, and what it does with them (a note, a CV) still plays.
Following the sound with no code
bind ties a number or a colour of an object to something that changes, and the canvas moves it on every frame by itself, while the code does nothing:
js
c.circle({ x: 240, y: 150, r: 20, fill: "#4fd1ff",
bind: { r: ["levelA", 20, 80], // bigger as In A gets louder
fill: ["bass", "#203040", "#4fd1ff"], // lighter with the bass
rotation: ["time", 0, 360, 0.25] } }); // round once every 4 secondsEach one is [source, from, to]: the source goes from 0 to 1, and the property goes from from to to with it (two colours for a colour). The sources:
| Source | What it follows |
|---|---|
| levelA, levelB | How loud the Scene's In A or In B is: 0 at -60 dB and under, 1 at 0 dB. It rises at once and falls back over a quarter of a second. |
| bass, mid, treble | In A's lows (up to about 250 Hz), mids (up to 4 kHz) and highs (up to 16 kHz). |
| P1 to P60 | The canvas's P controls, and whatever is wired into them. |
| time | The canvas's clock, going from 0 to 1 over and over, as many times a second as a fourth number says (once when there is none): ["time", 0, 360, 0.5] turns it round every two seconds. |
| a zone's name | That zone's value: one of the canvas's own, or a control's made with param: true. "Pad.y" is the Y of an XY pad or a drag, and "zone:time" reads a zone that is called time. |
Numbers such as x, y, w, h, r, rotation, scale, opacity, strokeWidth, glow, blur, shadowX, shadowY, shadowBlur, patternX, patternY, sides and gain can be bound, and so can the colours fill, stroke, glowColor and shadowColor. While a property is bound, the source decides it, whatever the code sets. A new bind replaces the last one, and o.set({ bind: null }) lets them all go.
A text can show the number: bind: { text: ["levelA", -60, 0, { decimals: 1, suffix: " dB" }] } writes the source's value, laid from from to to, with so many decimals (2 when it does not say), after a prefix and before a suffix, on every frame. The same options can hold the time's rate: ["time", 0, 10, { rate: 0.1 }]. And smooth, in seconds, makes what is bound glide after its source instead of jumping: { smooth: 0.1 } follows most of the way in a tenth of a second, so a meter falls softly and a switch slides.
Ready-made controls
The canvas also has controls ready to place, one line each, drawn and played the same way whatever you build around them:
js
const c = canvas(1);
c.size(480, 300);
c.background("#0d1017");
c.knob({ x: 70, y: 110, name: "Cutoff", min: 20, max: 20000, log: true, unit: " Hz", decimals: 0, out: 2 });
c.slider({ x: 180, y: 80, w: 200, name: "Drive", out: 3 });
c.toggle({ x: 180, y: 140, name: "Glide", out: 4 });
c.meter({ x: 420, y: 60, h: 120, input: 1 });| Maker | What it is | Its own options |
|---|---|---|
| c.knob({...}) | A knob: drag up or down (Shift for fine steps), turn the wheel, or double-click for its default | size |
| c.slider({...}) | A slider: press anywhere on it, and drag | w, h, vertical (it stands up when it is taller than wide) |
| c.button({...}) | A button | w, h, mode: "trigger" sends an Event when it is pressed, "momentary" sends true while it is held and false when it is let go |
| c.toggle({...}) | A switch a click turns on and off | w, h |
| c.xy({...}) | A pad with a point you drag: its value is [x, y] | w, h, outX, outY (each on its own output) |
| c.meter({...}) | A level to watch, set from an input, with a line that holds the highest | w, h, vertical |
| c.steps({...}) | A row of cells a click turns on and off: its value is the list of them | count, w, h, gap; steps.light(i) lights cell i as the one playing |
| c.select({...}) | A row of choices, one lit: its value is its number, from 0 | options (their names), w, h |
Every control also takes x and y (its top left, or a knob's middle), name (shown under it, and the name its value is kept under with the project, so it is where you left it when the project opens), label (shown instead of the name; "" shows none), value (where it starts), default (what a double-click brings back), min and max (its range, 0 to 1 unless you say), step, log (a range for frequencies), unit, decimals, color (its own accent), tooltip, out: n (its value goes out of Out n whenever it changes, and once when the code starts), input: n (a message on In n sets it, and goes on out, as a Knob's In does) and onChange (a function called with each new value). g.knob({...}) and the others put a control inside a group.
With param: true a control is also one of the canvas's zones, by its name: the canvas gets an output named after it, your DAW gets a parameter to automate (with the canvas's Expose to DAW on), and its value is saved with the canvas and kept in its States. The control starts from that zone, and follows it when the DAW, a State or the canvas's Set input moves it; grabbing the control is a gesture for the DAW, so its touch and latch automation record what you play. Such zones have no place of their own on the canvas and do not show in Edit Zones: the Script's drawing is what the mouse plays. A meter and steps cannot be one.
Each maker gives the control back: its value can be read or set (knob.value = 0.3), knob.on("change", fn) calls fn with each new value, knob.group is the group it is drawn in (to move it, or animate it), and knob.remove() takes it away. c.theme({ accent: "#ff7a45" }) changes the look of the controls made after it: accent, body, edge, track, text, label and font (the size of their text).
A control of your own is a class built on canvas.Component: build() makes its drawing in this.group once, and draw() shows this.value; this.set(v) gives it a new value, which is kept, sent and drawn like any control's.
js
class Lamp extends canvas.Component {
build() {
this.bulb = this.group.circle({ r: 12, fill: "#333" });
this.group.on("click", () => this.set(this.value ? 0 : 1));
}
draw() {
this.bulb.set({ fill: this.value ? "#ffcc33" : "#333" });
}
}
c.add(Lamp, { x: 400, y: 200, name: "Lamp", out: 5 });