⚡️ TL;DR
- Use the
#
token for simple cases where you want to harcode the multiparm instance index into the default value of a parameter (can cause issues when reordering). - For non-nested multiparms you can use
opdigits($CH)
as a quick way to get the multiparm instance index in Hscript. - For nested multiparms use
hou.Parm.multiParmInstanceIndices()
to get a tuple of multiparm instances.
Written for H21, but applies to previous versions of Houdini as well.
I often need to use multiparms when creating tools, and a question that comes up from time to time is:
How do I set the default value of a parameter in a multiparm that references another parameter in the same multiparm instance?
Why would you want to do that anyway?
- Your parameter value needs to be the result of combining some other related parameters.
- You want to have a unique label for each multiparm based on some other parameter values.
Let’s say we have a multiparm interface like this one, and we want the Voxel Size parameter in each multiparm instance to always default to being the product of each Grid Scale and Particle Separation parameter:

Example scenario
At first it seems easy enough, but in older versions of Houdini, inserting or deleting multiparm instances often broke default channel references. With Houdini 21 letting us easily shuffle around multiparms, this issue might show up more, so let’s check out how we can make sure our defaults are stable.
But first, Some Quick Facts About Multiparms
- Each time you increment/decrement the multiparm number, you create/remove a multiparm instance.
- Houdini adds a number or index to each instance.
- In the Parameter Editor, multiparms are templates, and the parm name itself has a hash in it, which gets replaced by the multiparm instance index.
- Nested multiparms use multiple hash tokens to denote their level. Something like
#_#
for the third element in the second multi-parm would become2_3
. - Prior to H21, multiparms could not be re-ordered. Only insert, append, and remove operations were supported.
- Hash tokens
#
can be used in default parameter expressions, and the default value for the parameter instance will have the index hardcoded in its default value (more on that below).
Problem Overview
How can we set the default value for the Voxel Size parameter so that it always references the Particle Separation and Grid Size parameters from the same multiparm?
Let’s keep using the example above to explore some options.
First Attempt: Hash # Token
Parameters inside multiparm blocks are required to have a #
in their name (if you don’t add one, it will be added for you automatically). This #
character can be used elsewhere too, like in the Disable When / Hide When Condition and the Default Value.
Literal Default
You can use the #
token as the default value for a parameter in a multiparm block. This will hardcode the multiparm index into that parameter instance’s default value

#
token literal default

#
as the parameter default. Notice how none of the parameters are bold, meaning they are all at their default value.
Hscript Default
Hopping back to our original example, we can use the #
token to create default channel references.

#
token Hscript setup
- Select the parameter.
- Go to the Channels tab.
- Change the Default Value’s Expression Language to Hscript.
- Set the expression using the
#
token in the parameter names.1
ch("psep#") * ch("gridscale#")

Each Voxel Size parameter looks correct and references the parameters from the same instance!
Though if you look at the default expression for each parameter, you’ll see that the #
token has been replaced by the multiparm instance’s index:

#
token has been replaced…
|
|
At first this might seem like no biggie. You might never notice it if you only add or remove instances from the bottom using the [+,-,Clear] buttons.
But what happens if we start to reorder these? Let’s move Sim 3 to Sim 1’s position:

Drag Sim 3 to Sim 1

Something’s not right…
Well that’s not right now is it? You would expect the value of Voxel Size to stay the same as it was when it was at the bottom before reordering. Now it appears to be saying that 0.05 * 1.0 = 1.0
? Also, all of the Voxel Size parameters for every other parameter have now gone bold, meaning they’re no longer at their default value. So what’s going on?
If we take a look at the expressions we’ll see that they kept their old values, so the channel references are pointing to the wrong parameters now:

Channel refs are still using their old index.
One solution would be to revert each Voxel Size parameter to its default:

Right Click → Revert to Defaults
But this is pretty unreasonable to ask people to do, especially when there are many parameters. There has to be a better way!
Pros & Cons
- ✅ Can be useful for simple cases where you want the value to match the index number.
- ✅ Hardcodes the index digit into the parameter’s default value.
- ✅ Easy to set up for nested multiparms.
- ❌ Hardcodes the index digit into the parameter’s default value.
- ❌ Can lead to unexpected behavior when the order of the instances in the multiparm block changes, since the index the instance was created as is hardcoded into the default value and does not update when re-ordered.
Hscript Solution: Use $CH
Since the #
character is replaced with the multiparm index, we can’t rely on it for relative references when reordering because the parameter name itself updates.
So we need something a bit more flexible. We need to be able to look at the current parameter and get its multiparm index. This is straightforward in Python, but before we start jumping there let’s see if there’s a simpler way with Hscript. Since the index is part of the parameter’s name, maybe we should look there first. But how can we get the parameter’s name?
AFAIK there is no Hscript function like opname
for parameters - but there is a variable $CH
, which will return the current channel name!
$CH
variable evaluates to the current channel (parm) name.
opdigits
and $CH
Now that we have the parameter name, we need to extract the index number from it. This is a perfect job for our friend opdigits()
.
“…return the numeric value of the last set of consecutive digits in a node’s1 name”
1 Despite the wording in the docs, you can use this on any string, not just node paths.
We can use this to quickly grab the index from the parameter name. Since our multiparms have the #
token at the end and no other digits after it will work just fine:
|
|
opdigits($CH)
for a parameter called int_31415
Solution
Let’s apply this to our multiparm example from earlier:

Edit Parameter Interface
- Select the parameter.
- Head to the Channels tab.
- Set the Default Expression Language to Hscript.
- Set the default expression using
opdigits($CH)
instead of the#
token:
|
|
or
|
|

New default expression using opdigits($CH)
instead of #
.

Updated expression with opdigits($CH)
evaluated.

What happens if we move Sim 3 to Sim 1’s spot this time? Everything is still correct after reordering!
Pros & Cons
- ✅ Index is not hardcoded. Works no matter if you reorder/append/delete multiparm instances.
- ✅ Easy and quick with Hscript, no Python required.
- ❌ Does not support nested multiparms well.
- ❌ Depending on the parm name, this could break depending on where the hash character is in the name if there are numbers in the parm name already, either from a user or a parm with size>1 (see below).
- ❌ Can be cumbersome to write Hscript sometimes.
Limitations of opdigits($CH)
While the opdigits($CH)
solution is quick to implement and works pretty well in a lot of cases, it does have a few drawbacks.
❌ Numbers in Parameter Names
Sometimes parameters will already have digits in their names. This could cause a problem depending on where the digit is in the name.
User-created Parameter Names
When the digit is part of your own naming scheme, it’s usually easier for you to just change it. If you absolutely must have a digit in the name, try to make sure the #
token is at the end of the parameter name if possible:

❌ Don’t put your digit after the #
token if you can help it.

✅ Keep the hash at the end if you can.
Float & Integer Parameters With Size > 1
It gets trickier with Float or Integer parameters that have a size greater than 1, because Houdini automatically appends a digit to the end of each one:

Houdini adds a digit for each parameter inside the parmtuple.
One possible workaround could be to instead use the Float Vector or Integer Vector parameter types instead if that suits you (assuming the size of the parameter is 2, 3, or 4).That way the parameters will get x,y,z,w
added to the end of the name instead of 1,2,3,4
.

Float/Integer Vector Parameter Types

1,2,3
becomes x,y,z
❌ Nested Multiparms
Remember that opdigits
only gives us the last consecutive digits of a string. When nesting multiparm blocks inside of other multiparm blocks, you need to add another #
token to the parameter name for each level of nesting. Most commonly this is done by adding an underscore and another #
:
|
|
In this case opdigits
won’t give us enough info.
Nested Multiparms
As mentioned above, our opdigits
trick breaks down once we start nesting multiparms.
Let’s update our scenario to see how we can handle nested multiparms. We’ll create an interface with one multiparm block that populates a list of sequences, and inside each sequence there is another multiparm block that populates shots.

Nested multiparm example.
In this case, let’s make sure that the default for Shot Name is always:
|
|
Hash Token Revisited
Take a look at the parameter names for the nested multiparm. Another _#
is added for each level of nesting:

Notice the double hash token for the nested multiparm #_#
.
We can try to do the same thing as we did before, only this time we’ll add the extra #
to our channel reference:

Nested multiparm Hscript default using #_#
token.
|
|

#_#
evaluated inside an Hscript default expression

The numbers are backwards! This feels like a bug 🐛.
The first instance looks correct, but it seems like the order of the #
tokens gets messed up as we add more. My guess is that since we used the #
token already in chs("seq#)
, that by the time it gets to chs("shot#_#")
the parser sees that we’ve used the 2 #
tokens already instead of treating each chs()
section separately. Switching the expression to a string literal and using backticks doesn’t help either, so we might be out of luck for this case!
Where Does it Work?
However this will work just fine as before if you’re referencing multiparms in the same nesting level, like so:

Using #_#
like this works.
|
|
🐍 Python to the Rescue
When all else fails or you have a particularly interesting scenario, you can always switch to a Python expression. hou.Parm
has several methods for dealing with multiparms, the most useful one in our case being hou.Parm.multiParmInstanceIndices
.
Houdini 21 added a few more multiparm methods. Check ’em out!
Solution: Using hou.Parm.multiParmInstanceIndices
to Find the Right Index

Default Python Expression for the Shot Name parameter returns the correct result now.
Here’s the final setup - I’ll step through the code in more detail below. Our new default expression looks something like this:
|
|
or shorter with nested f-strings (some might say this is a bit less readable):
|
|
Don’t forget to change the language of the default expression:

Set the Default Expression Language to Python 🐍.
Explanation
First we need to get a reference to the current parameter object, which we can do with hou.evaluatingParm()
:

Use evaluatingParm()
to get the hou.Parm
object for the parameter.
|
|
Then we use hou.Parm.multiParmInstanceIndices
to guarantee that we’re getting the correct multiparm index for our parameter. This function returns a tuple with all of the multiparm indices from top to bottom, similar to what you would get from the #
token:

multiParmInstanceIndices()
in action.
Once we have that we just need to return the value! In this case we grab the corresponding Sequence Name and Shot Number:
|
|
and squish them together:
|
|
Pros & Cons
- ✅ Can handle more scenarios.
- ✅ More flexible than Hscript.
- ❌ Requires a Python expression and knowledge of HOM.
- ❌ Needs a bit more care in exception handling to not be annoying depending on what you’re doing.
Conclusion
And there you have it! A few methods you can use for setting default values in multiparms that consistently refer to another multiparm regardless of reordering. I like to keep it simple and stick with Hscript as long as I can, but in the end you should be able to solve most challenges with a Python expression if you really need to.
Thanks for reading!