Some musings on World Machine's types and how it can become really powerful

I’ve been using World Machine (WM) for quite a while now, probably 8 years or so, and, as with most programs, the more experience you get with it, the more you can feel limited by it. That is not to say such “feeling of limitation” means the program is per se bad, but it does let you desire certain features.

So here is a roller-coaster of thoughts. Disclaimer, for those familiar with functional programming, terminology may be used incorrectly for illustrating purposes/making it easier to read/understand).

Last year I learned functional programming (FP), a type of programming paradigm, and with FP a core concept is that a function takes an input and produces an output and does nothing else. Another core concept is that a function will, given the same input always the same output. And while using WM, a thing I started to notice is that WM is almost like a FP language, where each device is a function and, given the same input, will always give you the same output. The devices’ parameters may be open to change, but it will always have some expected behaviour.

In FP, another important concept is the Type system. A type is a category of values that are the same, like the Integer type containing all integer numbers and the Float type containing all floating point numbers. The sweetness of these types is it lets you make assumptions over the data you’ll be working with and therefore setup a chain of actions (read: functions/devices). And, who would’ve thought, WM has a type system as well! The Heightfield type, the Bitmap type, the Water type, the Transform type etc.

So… why is this important? Again, in FP, these types allow you to make assumptions and predict behaviour. You know that, when getting an Integer type, it will always be an integer (like 42), and never be some text value (like “hey”), which is called Type Safety (and right now WM is kind of “type safe”, as you cannot link a bitmap to a device that only takes in a Heightfield). FP then takes it a step further and lets you, the user, create your own types, either from scratch or build up from existing types, and you can use existing functions that work with said type with your new type, as long as they abide the laws of that type (technically these are type classes), and this is were the interesting part comes.

In WM, the Composite Type lets you unpack types. The Water System type is made out of 3 types, two being a Heightfield and one being a Vector Field type (which in itself is made out of two Heightfield types). Another example is the Bitmap type. When using the Channel Splitter device, you actually unpack the type and get 3 Heightfield types, each representing a colour channel. Once you have a Heightfield type, you can use almost any device on it to transform it.

So… why is this important? Knowing this I came up with the following propositions.

  • Let users create custom types, which can be shared and added to WM kinda like extensions of the “core WM type system”.
  • Let users create interpretation of types with default behaviour.

The first proposition is nice for macros as it would let you create your own type, for example a Snow type that is made out of 2 Heightfields and a Parameter type, so you can exchange that Snow type with other macros without having to hook up 3 wires but instead only 1 wire. Less wiring! Oh and because of the Type Safety you now no longer can hook up the 2 Heightfields of the Snow type incorrectly, bonus!

The second proposal basically would enable you to manipulate a Bitmap type much easier because, instead of having to use a Channel Splitter device, then use a device on all three the channels and then again use a Channel Combiner device to create a Bitmap once again, you tell WM how to handle the Bitmap type.

huh?

You create rules/laws for your type and tell that, if you connect a Bitmap type to a Clamp, the clamp device knows “Oh, I first need to unpack this type into three channels, then clamp all the three channels separately and then repack them into a Bitmap!.”, instead of always having to wire Channel Splitters and such. You simply hook it up to the clamp device and it will work, because you have told WM how to handle your type and therefore does all the wiring in the back. This will only work, of course, if you tell WM how to handle your new type and it would be quite a painful experience if you always have to do a “demonstration hookup” of your type for every device in WM. Instead, it would be better if you could “proof” your type can be interpreted and so use the default behaviour and at the same time be able to define exceptions for certain devices.

For example, maybe you want your Snow type’s heightfields to always (default) be handled the same (that is, both get processed by the device that’s taking in the Snow type), except (exception) for when hooking it up to the clamp device, then you only want one of the two heightfields to be affected. Again, all this information would be stored in the Snow type and someone using your Snow type does not need to think about it as WM does all the heavy lifting in the back. Then, if someone really wants to use a clamp on both heightfields in the Snow type, they can always use the Composite Type device to unpack your type and then use a clamp device on both heightfields.

I hope this roller-coaster was somewhat understandable. If you have any questions, remarks or thoughts, I would love to hear them and discuss!

2 Likes

Indeed, the Composite type has been aimed with an eye to allowing user-creation of new types.

There are two complications, neither insurmountable, with user created types:

  1. As you mentioned above, telling WM how the type should be interpreted/displayed can be complicated. If you want automatic type packing/unpacking like in the example, it gets even more complicated.

  2. Custom types are most useful when they are standardized/interchangeable. Macros made by different people that create different custom types can’t ‘talk’ to each other, which limits the value generated. Other than creating a “standard library” of extended types that ship with WM, I’m not sure how to avoid this problem - but creating such a standard library removes much of the need for creating custom types in the first place!

Like I said, neither is insurmountable, but definitely some thought is needed.

Regarding your points:

  1. I don’t know how it would/could be implemented in WM’s code, but I think it is important to make it as simple and intuitive as possible in the UI. The default behaviour should be so that the “coretypes” a type is made out of, all “hook up” with the primary input of the device, get processed and then “go back into” the type. Let me illustrate this with the following type, the Snow type.
    The Snow type has 3 fields, 2 of type Heightfield and one of type Parameter, being snow surface (Heightfield), snow depth (Heightfield) and snow line (Parameter). The default behaviour would then be that, when connected to a Clamp device, both Heightfield types will be processed, unless told otherwise. When connected to a Parameter device, the Parameter type will be processed. The image below illustrates how it would work under the hood, but in the above the hood it’s just connecting a Snow type to a clamp and getting a Snow type out of it. Whether or not a custom type can hookup with a device depends on the custom type’s coretypes and behaviour.


    To create “excpetions”, the user will have to tell World Machine what input(s) to take. This can be via a dialogue or, to keep it intuitive, by simply dragging over wires. An advantage of the latter one, is it lets you create really powerful types with visual feedback, since you can “blueprint” their behaviour. In this example, the snow depth is not processed by the Clamp device, but instead used to mask the Clamp device’s effect.

    An important note: A custom type will not work if no behaviour/interpretation is set. Therefore, it won’t work with someone else’s macro unless you explicitly tell WM how to interpret your type when passed to a macro.

  2. I think it is inevitable different types will emerge and I don’t think that should be a reason not to implement it. Especially in the beginning, when people are figuring out how to work with the types and are playing around with it, lots of types will emerge. However, after some time, users will probably like one type more than the other, filtering out a lot of the early types, and with that prune the type diversity for the better. However, there is only one way to find out how that will develop.
    Creating a standard library would indeed remove the need for custom types and is therefore not something to opt for. However, if you want more control, maybe a community based committee that oversees the types and can add/remove/edit types in the “extended type library”. Users would still be able to create custom types, however, to share them in the “extended type library”, they need to get a green light from the committee. Macros could still be exported with custom types, but are only “supported” by WM if that custom type is also in the “extended type library”, and otherwise restricted to the macros of the type’s designer.
    For example, if my Snow type from above wouldn’t have gotten a green light, I could still distribute my macro with a Snow type output and another macro with a Snow type input, but only those two macros could handle/interpret the Snow type, and nothing else could. It still has massive benefit for those two macros, or maybe even more macros from a whole macro suite, but indeed, it would be limited to those macros only.
    I think it is best to just “let it loose”, as a committee can create potential conflicts when people’s types get denied, and let “natural selection” do its work on the types.

1 Like