So you've pushed past hscript, ignored python for now, and seen the awesomeness of vops. You'll also probably start to get curious as to why there's at least 4 different ways to define point attributes, all with various idosyncrasies. Time to sweep all that away with Vex, specifically with the Wrangle Sop.

Wrangle Sops let you write little bits of Vex code, and come with a few UI niceties to make them quick and efficient to use. They come in various flavours (point, prim, detail, attribute), they're all the same thing, just with the 'what do i operate on' menu switched to point/prim/detail.

Best way to get into vex and wrangle nodes is to put down a 40x40 grid, append a point wrangle, and follow along:

Create a new attribute

You want to define a new point float attribute 'foo'? Just type


Hit ctrl-enter, look in the geometry spreadsheet, there you go, float attribute. The @ tells houdini that you want this to be an attribute, the default type is float.

You want it initialised?


You want a vector attribute? Prepend 'v' before the '@'. To initialise, use curly braces if its just numbers, or use set() if you're doing something with functions or other attributes:

v@other=set(0, @P.x, 1);

You can set the attribute type using the appropriate prefix:

// common ones
f@foo = 12.234 // float
i@foo = 5 // int
v@myvector={1,0,3}; // vector

// less common ones
w@george;  // quaternion, a 4 value vector
m@transform; // matrix

// there's more, see the docs. :)

Get existing attribute values

You want myvector to get its values from the point position?


The @ symbol is used to get attributes as well as set them. All the attributes you see in the geometry spreadsheet, you can access them by sticking @ in front of their name.

You want it 1.5 times N?


To access an individual attribute of a vector or colour, use Eg to get just the y value of each point and divide in half:

@foo=@P.y / 2;

To set it is similar. Eg to set the red channel of each point to the sine of double the x position:

@Cd.r = sin( @P.x * 2 );

Vector attributes like Cd or P let you refer to components in several ways, use whatever is convenient. The component can be r/g/b, x/y/z, [0]/[1]/[2].

// These all do the same thing, set the first component of the vector:
@Cd.r = 1;
@Cd.x = 1;
@Cd[0] = 1;

// As are these, all setting the third component:

@P.z = 6;
@P.b = 6;
@P[2] = 6;

Implicit vs explicit attribute type

Note that wrangles implicitly know certain common attribute types (@P, @Cd, @N, @v, @orient), but if you have your own attributes, Houdini will assume its a float unless told otherwise, easy mistake to make.

To ensure Houdini does the right thing, prepend the type. Eg, you've set @mycolour as a vector, and try to use it in a wrangle:

// BAD
@Cd = @mycolour; // This will treat it as a float and only read the first value.

@Cd = v@mycolour; // Explicitly tells the wrangle that its a vector.

I always forget to do this, shout at my wrangles for a bit, then finally remember to stick 'v' in front of my attribute names. No doubt you'll do the same. :)

Create UI controls

You want a slider automatically created to scale the sine wave for the previous example?

@Cd.r = sin( @P.x *  chf('scale')   );

Hit the little plug icon to the right of the text editor, Houdini scans the vex code, realises you've referred to a channel that doesn't have a UI yet, and makes a channel at the bottom of the wrangle UI named 'scale'. Start sliding it around, you'll see the colours update.

There's several channel types you can use. ch() and chf() both create a float channel. For the others:

// An int channel
i@myint = chi('myint');

// A vector channel
v@myvector = chv('awesome');

// A ramp channel
f@foo = chramp('myramp',@P.x);

The last one is really handy, in one line you get to read one value (@P.x), remap it via the UI ramp widget, and feed that to @foo. I end up using loads of these all through my setups. It assumes the incoming values are in the 0-1 range, you often have to fit the values before feeding to the ramp. The fit function goes fit( value, oldmin, oldmax, newmin, newmax). Eg, remap X between -0.5 and 6, then use a ramp to feed those values into the red channel:

float tmp = fit(@P.x, -0.5,6,0,1);
@Cd.r = chramp('myramp',tmp);

But why stop there? Define the start and end points with UI sliders!

float min = ch('min');
float max = ch('max');
float tmp = fit(@P.x, min,max,0,1);
@Cd = 0;  // lazy way to reset the colour to black before the next step
@Cd.r = chramp('myramp',tmp);

Customise the UI elements

The plug button is a convenience function, it just scans for any channel references, and creates the default type, with a default value. Once the channel is made, you can on the wrangle node and choose 'edit parameter interface' (or use the gear menu from the parameter panel), and change float ranges, the default value, the labels, whatever you want.

A handy thing I often do is to convert the default vector channel to a colour channel. I'll make as many vector channels as I need in vex, eg:

v@colour1 = chv('col1');
v@colour2 = chv('col2');
v@colour3 = chv('col3');
v@colour4 = chv('col4');

Then I'll hit the plug button, edit the parameter interface, shift-select all the channels I made, and change the type field to 'color', and to be extra saucy, change 'show color as' to 'hsv sliders'. Most handy.

Common functions

These appear in 99% of my vex wrangles:

  • fit() - take a number between 2 values, fit it between 2 other values, usually 0-1. Eg, remap @u from range -5,20 to 0-1: foo = fit(@u, -5, 20, 0, 1);
  • rand() - generate a random number between 0 and 1. Usually feed it the point id, so each point gets a random number: foo = rand(@ptnum);
  • sin(), cos() - as you'd expect, but in radians.
  • radians() - convert a number from degrees to radians: foo = radians(90);
  • length() - measure the length of a vector. Eg, measure the distance of a point from the origin: dist = length(@P);

Built-in attributes

There's a few built in variables you can use, easiest way to see them is to put down a point vop, and look at the global params. Here's the more common ones:

  • @ptnum - the point id
  • @numpt - total number of points
  • @Time - current time, in seconds
  • @Frame - current frame
  • @primnum - the primitive id
  • @numprim - the total number of primitives


Best way to learn I found is to look at other peoples work. Been creating a bunch of examples on this odforce thread:

Random delete points by threshold

After Matt Ebb showed me this, I use it a million times a day. Scatter some points, put down a wrangle with this:

if ( rand(@ptnum) > ch('threshold') ) {

Use the plug button to create the threshold slider, now slide it up between 0 and 1, you'll see points get randomly deleted. What's going on:

  • rand(@ptnum) -- each point gets a random number between 0 and 1 based on its id
  • > ch('threshold') -- compare that random number to the threshold slider. If it's greater than the threshold...
  • removepoint(0,@ptnum) -- ...delete the point. 0 means the first input, ie whatever you've connected into this wrangle, and @ptnum is the reference to the point.

Wave deformer

A classic. Got your 40x40 grid ready? Good.

We'll make concentric sine waves that radiate from the center. That means we'll need to measure the distance of each point to the origin:

@d = length(@P);

We'll feed that to a sin(), and use that to directly set the y-position of each point:

@P.y = sin(@d);

Good, but the waves are too big for our default grid. Lets multiply @d by some factor, that'll give us more waves. Change the previous line to read:

@P.y = sin(@d*4);

Ok, but lets make that driven by a slider instead:

@P.y = sin(@d*ch('scale'));

Hit the plug button, play with the slider (you can push the slider past 1), see the waves move around.

To make the waves animate, add time to the inner term.

@P.y = sin(@d*ch('scale')+@Time);

wait, they're moving towards the center. Lets make it negative time:

@P.y = sin(@d*ch('scale')-@Time);

Or better, control it by a slider so you can drive the speed:

@P.y = sin(@d*ch('scale')+(ch('speed')*@Time));

That's messy, lets tidy up the whole thing. Nice thing about wrangles is you can split across multiple lines for readability:

@d = length(@P);
@d *= ch('scale');
@speed = @Time*ch('speed');
@P.y = sin(@d+@speed);

Lets add 2 more things, a control for the maximum height, and a distance based falloff.

Height is easy, whatever the final result is, we'll multiply it by a channel reference; if its 1 it'll be unchanged, 0 will cancel out, other numbers will scale appropriately:

@P.y *= ch('height');

And finally a ramp to control the falloff. A ramp widget expects a variable in the 0-1 range, so the first thing we'll do is take the distance variable, and use fit() to map it between 1 and 0 (note we go 1 0, not 0 1; we want the center to be of maximum height). The start and end points will be controlled by channels:

@falloff = fit(@d, ch('start') , ch('end') , 1,0);

A ramp channel expects a name and a variable, the variable will be remapped through the ramp curve. We'll take the result and directly multiply it against P.y:

@P.y *= chramp('falloff', @falloff);

A minor annoyance here is that I expected the start and end to be in worldspace units, but the 'end' channel was a large number. I realised that's because we'd already multiplied d by the scaling channel to control the number of waves. A quick reshuffle of the code fixed that, here's the end result:

@d = length(@P);
@speed = @Time * ch('speed');
@falloff = fit(@d, ch('start'), ch('end'),1,0);

@P.y = sin(@d*ch('scale')+@speed);
@P.y *= ch('height');
@P.y *= chramp('falloff', @falloff);

Attributes vs variables

The examples given above all use the @ syntax to get and set attributes on points. If you have more than a few wrangles, or if you don't need those attributes outside the wrangle, this is a needless amount of data you're throwing around.

Instead, you can create variables that only exist within the wrangle. The syntax is similar to other c-style languages; define the type with a full word, and don't use a prefix on the variable:

float foo;
vector bar = {0,0,0};
matrix m;

foo = 8;
bar.x += foo;

Point attributes and vex variables live in seperate worlds, so you can have one of each with the same name, and they happily co-exist. I tend to avoid this in practice, easy to add an @ where you didn't mean, or remove one where you need it, and break things:

float dist = length(@P);  // a local vex variable that only exists within this wrangle

@dist = dist;  // create a new point attribute, @dist, and assign the local variable 'dist' to it. Worlds collide!

Rewriting the previous example to use variables, and do the formal thing of declaring variables first so its clear to see whats going on:

float d;
float speed;
float falloff;
d = length(@P);
speed = @Time * ch('speed');
falloff = fit(d, ch('start'), ch('end'),1,0);
@P.y = sin(d*ch('scale')+speed);
@P.y *= ch('height');
@P.y *= chramp('falloff', falloff);

Wrangles vs hscript vs vops vs everything else

Vex is as a general rule always going to be faster than using hscript, and will scale much better.

Vops are great, but often its the simple things that you can do in 2 lines in vex, that would take 7 or 8 nodes in vops. That said, certain operations are easier in vops, like using noise or loading other patterns, or building stuff when you don't exactly know what you're aiming for yet. After you're done, you can always re-write your setup in a wrangle if you think its better.

Re other sops, a wrangle can do the work of several other sops, often more efficiently, with the ability to make UI elements easily. Some examples:

  • point sop - a lot of older tuts use point sops. Don't. Just say no.
  • attribcreate sop - faster and easier in a wrangle. The only reason you might wanna use an attribcreate is to get local variables, but you'd only use them with a point sop, which we already agreed suck.
  • vop bind and vop bind export - getting and setting point attributes is way easer with @ shortcuts than in vops.
  • promote parameters in vops - again, ch('foo') vs, bind, fiddle with names, realise its wrong, get parameters in a semi hung state, ugh
  • channel referencing in hscript - the auto binding with ch() is the bees knees. That little plug button is the best thing ever.

That's being a little trite, all sops and vops are useful at some point, but the speed and elegance of wrangles is hard to beat.

Mixing vex and vops with inline code

Often I'll start something in vops, then realise part of my network would be much neater as a few lines of vex. When that happens, I'll drop in an 'inline code' vop, and I get something that looks very similar to a wrangle node. I'll then code whatever I need to code, and it happily co-exists with the vops around it.

The syntax is a little different, took a few goes to get used to it. First is that you don't need the @'s, so @P.y is just P.y.

The second is how to define the output of the node. By default there are no outputs, at the bottom of the parameter interface you go to the first disabled output, set its type (eg vector), and give it a name (eg, P). You then see a 'P' appear on the right side of the node, ready for you to connect to other things. Cos I'm an idiot, I tend to name these outputs explicitly, eg 'outP' rather than 'P'.

To assign values to that output, use a $ prefix. Eg, here I want to drive P.y by Cd.r. I've defined the output as outP on the UI.


Note that setting P.y like that in a wrangle (with the @ prefix of course) would immediately update the geo, it doesn't in an inline code vop. P here just exists within the node; we then have to be able to export it via outP, then plug it to the global output P variable to see any effect.