There's also the Extended Wave modifier, also in Reactor, which adds a couple more options, like the ability to reverse the wave. Not needed in this instance though.
banana, I know you said you didn't want to use the Spline Editor, but just checking you were aware that you can achieve the loop part of this (not the custom control part) - with two keyframes and the Set Loop -> Loop option:

This has the advantage that you could, if desired, set easing on the transition from lower to upper value and that would be automatically replicated in the loop. But yeah it doesn't easily work with custom controls for upper/lower/period, you'd need to edit the keyframe values and timing to change the loop.
Below is a Background node with its four colour controls (TopLeftRed, TopLeftGreen, TopLeftBlue, TopLeftAlpha) animated in four different ways. It has three custom controls - LoopPeriod, LoopLower, LoopUpper - to control this:
- TopLeftRed = Wave modifier (requires installation from Reactor)
- Period -> Connect To -> LoopPeriod.
- Offset -> Connect To -> LoopLower
- Strength -> SimpleExpression -> "LoopUpper - LoopLower" (could also have used a Calculation modifier)
- TopLeftGreen = Keyframes with Loop option as described above. Looping from 0 to 1.0, not connected to the custom controls.
- TopLeftBlue = Expression Modifier, implementing an expression to loop between LoopLower and LoopUpper in LoopPeriod.
- LoopLower, LoopUpper and LoopPeriod are Connect To into n1, n2 and n3 of the modifier.
- Number Out = an expression to loop between these values (probably not the most elegant expression, but seems to work)
- TopLeftAlpha = SimpleExpression. Same expression as in the Modifier, but referencing the three custom controls directly
So pick your poison as to which method you like most. In this example the Expression Modifier doesn't do anything that the SimpleExpression doesn't also do, but the modifier is more work to set up. I think the only reason to consider an Expression Modifier is that it could then be further modified (eg if you wanted some randomness via a Perturb), and because it has the unique ability to keyframe the expression, allowing different expressions at different times.
- Code: Select all
{
Tools = ordered() {
Background1 = Background {
CtrlWZoom = false,
Inputs = {
GlobalOut = Input { Value = 100, },
Width = Input { Value = 1920, },
Height = Input { Value = 1080, },
["Gamut.SLogVersion"] = Input { Value = FuID { "SLog2" }, },
TopLeftRed = Input {
SourceOp = "WaveModifier1",
Source = "Result",
},
TopLeftGreen = Input {
SourceOp = "Background1TopLeftGreen",
Source = "Value",
},
TopLeftBlue = Input {
SourceOp = "Expression1",
Source = "NumberResult",
},
TopLeftAlpha = Input {
Value = 20,
Expression = "(time % LoopPeriod / LoopPeriod * (LoopUpper - LoopLower)) + LoopLower",
},
LoopLower = Input {
SourceOp = "Publish1",
Source = "Value",
},
LoopUpper = Input {
SourceOp = "Publish2",
Source = "Value",
},
LoopPeriod = Input {
SourceOp = "Publish3",
Source = "Value",
},
},
ViewInfo = OperatorInfo { Pos = { 948, 147.97 } },
UserControls = ordered() {
LoopLower = {
LINKS_Name = "LoopLower",
LINKID_DataType = "Number",
INPID_InputControl = "SliderControl",
INP_Integer = false,
INP_MinScale = 0,
INP_MaxScale = 1,
ICS_ControlPage = "Color",
},
LoopUpper = {
LINKS_Name = "LoopUpper",
LINKID_DataType = "Number",
INPID_InputControl = "SliderControl",
INP_Integer = false,
INP_MinScale = 0,
INP_MaxScale = 1,
ICS_ControlPage = "Color",
},
LoopPeriod = {
LINKS_Name = "LoopPeriod",
LINKID_DataType = "Number",
INPID_InputControl = "SliderControl",
INP_Integer = true,
INP_MinScale = 1,
INP_MaxScale = 1000,
INP_MinAllowed = 1,
INP_MaxAllowed = 1000000,
ICS_ControlPage = "Color"
}
}
},
WaveModifier1 = Fuse.WaveModifier {
CtrlWZoom = false,
Inputs = {
Wave = Input { Value = FuID { "Saw" }, },
Period = Input {
SourceOp = "Publish3",
Source = "Value",
},
Strength = Input {
Value = 10,
Expression = "Background1.LoopUpper-Background1.LoopLower",
},
Offset = Input {
SourceOp = "Publish1",
Source = "Value",
},
Limit = Input { Value = 0, },
},
Version = 100
},
Publish3 = PublishNumber {
CtrlWZoom = false,
Inputs = {
Value = Input { Value = 10, },
},
},
Publish1 = PublishNumber {
CtrlWZoom = false,
Inputs = {
Value = Input { Value = 20, },
},
},
Background1TopLeftGreen = BezierSpline {
SplineColor = { Red = 0, Green = 255, Blue = 0 },
NameSet = true,
KeyFrames = {
[0] = { 0, RH = { 3.33333333333333, 0.333333333333333 }, Flags = { Linear = true, Loop = true } },
[10] = { 1, LH = { 6.66666666666667, 0.666666666666667 }, Flags = { Linear = true, Loop = true } }
}
},
Expression1 = Expression {
CtrlWZoom = false,
Inputs = {
n1 = Input {
SourceOp = "Publish1",
Source = "Value",
},
n2 = Input {
SourceOp = "Publish2",
Source = "Value",
},
n3 = Input {
SourceOp = "Publish3",
Source = "Value",
},
NumberExpression = Input { Value = "(time % n3 / n3) * (n2 - n1) + n1", },
NumberControls = Input { Value = 1, },
NameforNumber1 = Input { Value = "Lower", },
NameforNumber2 = Input { Value = "Upper", },
NameforNumber3 = Input { Value = "Loop Period", },
ShowNumber4 = Input { Value = 0, },
ShowNumber5 = Input { Value = 0, },
ShowNumber6 = Input { Value = 0, },
ShowNumber7 = Input { Value = 0, },
ShowNumber8 = Input { Value = 0, },
ShowNumber9 = Input { Value = 0, },
PointControls = Input { Value = 1, },
ShowPoint1 = Input { Value = 0, },
ShowPoint2 = Input { Value = 0, },
ShowPoint3 = Input { Value = 0, },
ShowPoint4 = Input { Value = 0, },
ShowPoint5 = Input { Value = 0, },
ShowPoint6 = Input { Value = 0, },
ShowPoint7 = Input { Value = 0, },
ShowPoint8 = Input { Value = 0, },
ShowPoint9 = Input { Value = 0, },
},
},
Publish2 = PublishNumber {
CtrlWZoom = false,
Inputs = {
Value = Input { Value = 30, },
},
}
},
ActiveTool = "Background1"
}