Endless Knob

Endless Knob

V3 2024-03-11 —added turns ratio input for sped up or vernier behavior.

Issues from output “k” continuous values, increasing or decreasing by 1 each time around. As for regular knob nodes, clockwise rotation is a positive change. Unlike regular knob nodes, these are adjusted by rotation about their centers. Small changes can be made by dragging the knob anywhere along or near its edge.

The value of k and the knob position are optionally saved and will be restored at next startup. A sample/hold node allows this memory. To make the memory feature easily optional, two S/H nodes are used, one with save turned on and one off, so that you can set the option without plowing through the guts of the module.

This knob can be truly “endless” or it can be set to work over a limited range by applying a 1 at “Limits,” using the values at Lim1 and Lim2, making the higher of the two the upper limit, etc. (Don’t forget that an open input is interpreted as one of the limits)

In addition to the k output, the “∂k” output provides the stream of distance samples and the “Adj” issues a 1 while the knob is being touched.

Reset the knob to the value at input “Reset Val” by taking the “Reset” input high.

Set the home position (pointer position when output is zero) by applying a value to input “Home Pos.”

This module is not yet assignable, as I don’t have a continuous controller knob at this point for testing.

This knob also lacks any direct-wire connectivity that knob nodes currently feature. You gotta crank on this one!

Operation: This module uses point-to-point wiring, which proved far more CPU cycle friendly than DSP node, but the operation is similar.

1: waiting: Capture and verify initial touch, capture initial x and y and make sure it’s close enough to the knob. Compute first angle using arc-tangent (atan2) function.

To allow closer positioning to adjacent knobs such that their xy touchpads overlap somewhat, a touch is accepted only if it’s close to the knob. This is calculated by comparing the knob-center-to-touch distance with sum of knob radius, “kradius,” and an arbitrary finger width which is intended to allow adjustment of values by flicking at or near the edge of the knob. To keep the active region circular for large knobs, the valid touch radius can not grow larger than half the width of the Canvas node (and matching touch pad.)

2: collecting - if still touching, collect subsequent x and y, compute new angle via atan2 function, find the difference between samples, then compute tangent on the difference, which represents how far you’ve moved along the unit circle (analogous to the derivative.) Add this distance to the previous knob position (analogous to integration). If not still touching, go back to a “waiting” state.

“Reset” high will in both operating states set the output to the value applied to the “Reset Val” input and set the state machine to “waiting.” Note that it’s possible to reset to a value outside of the limits (if the knob has limits set, of course). This won’t break it, but with any subsequent adjustment the output will pop to one of the limits.


Input Signal Range Notes
kRadius 10-40 Knob radius (upper value limited by xy touch pad size. Defaults to standard knob radius.
Reset 0/1 Held high, resets knob output to “Reset Value,” below.
Reset Value +/- any value
Home Pos +/- any value the fractional part sets position where k = 0
Save@close 0/1 If 1, saves knob position and value at close
Turns Ratio 0 to any value. Positive 1 gives visible rotation that matches the angle input. Negative values reverse the knob and output summing.
Limits 0/1 If 1, applies limits at Lim1 and Lim2
lim1/2 +/- any value Used as lower and upper limits. Order is not important for these inputs; the lower of the two is used as the lower limit.

Output Signal Range Notes
k +/- any value knob output, in revolutions, one rev per turn
∂k +/- small values angular velocity stream, revolutions/sample.
Adj 0/1 1 when the knob is being adjusted.

Version History

Revision | File
Endless Knob v3-1.audulus4 (16.9 KB)

Endless Knob v2-8.audulus4 (16.5 KB)

2| Original, DSP node-based. Original features, no save.
1 | Endless Knob 2024-02-06-01.audulus4 (8.3 KB) | 2/6/2024


Endless Knob v3-1 Demos.audulus4 (42.2 KB)


Love this.

One comment – chances are that with a knob you do not need per sample processing. If so, you can save on CPU by processing at control rate (block rate instead of per sample) using fill(). It’s easy to convert – instead of using the for i=1,frames loop, use the first sample at each buffer for inputs, for ex., touch[1] instead of touch[i], then output as fill(output, knobValue) ).

1 Like

Thanks. I’ve learned a lot from this, and have experienced some strange behavior due I think to my naive assumptions on the characteristics of the xy pad and not accounting for the fact that the xy pad updates at far below audio rate. I’m working on a version that will properly address it. Building with expression nodes might be more appropriate for this module, but right now it’s fun implementing mostly in Lua.

On my iPad the update rate of the xy pad node is about 5.3ms

I’m definitely going to try your suggestion. Lots of time spent waiting at sample rate for a new value out of the xy pad!

Yeah, if your audio buffer size is set to 256, update rate would be about the same anyway. Curious, how did you measure the update rate?

I used my o scope!

1 Like

Way better and no mysteries, but it still seems like quite the processor hog. Here’s what I have:
Endless Knob v2-5.audulus4 (12.0 KB)

It adds memory and optional limits.

Will rework and post properly in a day or two, hopefully!
BTW, I made this knob because I wanted a fairly unlimited trigger delay for my o-scope and needed a control that allowed that in a small space. I have always enjoyed the control I have over the volume of my stereo receiver, which I imagine uses a shaft encoder with a flywheel and impressively big knob. It took a zillion turns of the knob to go from -80 db to zero.

All useless if I don’t get the %CPU under control.

New version, now with point to point innards. Lighter on the CPU!

V3 uploaded. I added a “turns ratio” input to allow (generally) down-shifting the rate of accumulation of the sum per rotation angle. Default is 1:1. Higher values speed up the knob (within reason!) and lower speeds give a kind of vernier mode, which I needed so I could set sweep delay precisely in my oscilloscope.

Nice work!

I see that you abandoned the DSP node versions you had up and running in v2.5. I’ll add a mention that the difference between using DSP node fill() vs sample processing with the frames loop (for i=1,frames do end) should be quite clear – control rate should get you several times better CPU – and if you are using fill() within the frames loop, you may not get the benefits of control rate since it would be processing each fill() per sample.

I’m not 100% on mixing and matching control rate / per sample processing – so far I have been careful to keep the processes separate at final outputs, use either fill() for control rate or the frames loop to process per sample. (And building some stuff with a “control rate” version, then toggling between).