[ANNOUNCE] node-red-contrib-ui-joystick : beta

Hi folks,

Recently there was an interesting discussion about a joystick for Node-RED. My first thought was: "how cool would it be if we could use a (virtual) joystick to move around a PTZ capable IP camera". Or is it just me having watched to many programs like CSI NY and Mission Impossible :thinking:

Since it was the last day of the new year weekend, I decided to have sun fun with nipplejs. Thanks to @Steve-Mcl for the pointer to that project!!!

The result is node-red-contrib-ui-joystick which can show a virtual joystick in your Node-RED dashboard :champagne: :clinking_glasses: :partying_face:.

A series of properties can be customized on the config screen:


On my readme page you can find the following flow, which feeds the output of the joystick node to the Onvif PTZ node:


Which allows me to control my IP camera via the joystick:


Note that this is a quick demo that I created in a small amount of time, only to show the possibilities of a joystick in Node-RED. If anybody has time to improve this flow, please share your results!!!!

As always "constructive" feedback is very welcome!

Have fun with it!


If anybody wants to experiment with it, you can install it from my Github repo from within your .node-red directory (since the beta is not available on NPM yet):

npm install bartbutenaers/node-red-contrib-ui-joystick
1 Like

Interesting - yes I'm sure folk will find uses for that !
not sure why it needs to create quite so much data though - are the raw, vector, and position properties actually useful ? And both radians and degrees ? And what is force ? Is that just how far you have pushed it ? = some measure of distance ?
And what are all the checkboxes and timings for ? why would I want them ? Can you simplify that for starters ? It's always easier to add things later if they are needed, rather than lump everything in and confuse users on day one. Just because the underlying library has lots of info - doesn't mean it's all useful to Node-RED use cases :slight_smile:

1 Like

Yes nipplejs indeed generates a lot of events, when the option "Send output msg at every change" is activated. Have been thinking today if I should allow some kind of throttling, but had not enough time... E.g. a minimum distance between successive events, and ignore all events in between in the frontend.

Well to be honest, I didn't use those myself. But thought to send them anyway in the output message, to support other use cases. But not sure if that is useful or not ...
I can imagine that somebody wants to have the position or the vector.

Indeed the same info duplicated. But it might be pity if some users need to add an extra node in their flow to convert (between radians and angles), since nipplejs offers them both...

So yes the output message might contain too much information. In fact I had no explicit reason to keep it like that. Only reason is that I had not enought time to examin all the output fields in detail. And starting from tomorrow, the free time will start shrinking again significantly :roll_eyes: Will try to see whether I can skip some data...

That is something I cannot remove, since I added those features for my own purpose (i.e. not part of nipplejs). I needed those timers for my cameras: e.g. when you move to joystick to the left, nipplejs will generate ONCE a message with left. Which means my PTZ node will move ONCE to the left. But when I KEEP HOLDING the joystick to the left, I want to KEEP generating messages until the joystick is released (to make sure that the camera keeps moving to the left).
Therefore you need to be able to specify a timer interval, and that timer is automatically stopped when the joystick is released (based on a nipplejs event). My flow would become too complex if I would like to do that outside of the node.

Since this is not obvious, I will need to explain better on my readme page what the timers are being used for. By default the timer intervals are 0, which means no timer...

Thanks for the detailed feedback!!

Aha. So I would say start with just one time interval and use that to throttle all the outputs (including the underlying data). And default it to something like 20mS otherwise it is a firehouse. ( I think that is what things like the slider default to )

The vector and raw look more like html/ pixel stuff. No need to expose that.

I don’t buy the radians degrees argument as I have yet to see a node that takes raw radians, let alone on a sub-property and as soon as you need a function node you have all the conversions to hand.

Ok throttling based on time. Make sense. I had thought to do throttling based on (some?) distance, but time indeed sounds logical.

Although I think throttling is only useful for option 1:


Option 2 and 3 send much less messages: only when you arrive in a new quadrant, e.g. when you go from right to left, or up to down. I assume people want to know all intermediate states where the joystick has been positioned. The number of messages will be rather limited in those cases, unless you get an epileptic attack :wink:

So option 1 will get a throttle interval, and option 2 and 3 will have a resend interval...

It was worth trying ...

having played some more.... (always dangerous)... I can see that in fact the vector is actually the useful part :slight_smile: as that tells you percentage wise both the x and y offsets. So for a joystick all others are either redundant or derivable from there. Even for your use case I would have thought you would want an amount of movement proportional to the amount of stick movement ?

Also what is distance ? imho it should be (again) a percentage of the max in that direction.

Not sure what you mean by less messages for 2 and 3 - if you set a resend interval you are then creating a rate - whether that is generated by reduction or addition doesn't make a difference. Indeed I notice if I hold the stick still fully to one way then the underlying library does stop sending - so again I would suggest you just have an overall rate... so it always sends at a rate if not at position 0,0. I think then you could do away with your extra modes as you can easily tell if the vector is +ve or negative and thus which way to move. (and indeed if above or below a threshold)

So net is I think all you really need is an overall rate when not at 0,0, and to just return the vector. (and ok maybe the direction (but as a %) and angle just as degrees )

Oh no :wink:

That would indeed perhaps be a better approach. But as I mentioned yesterday, I quickly created that demo (less than 10 minutes) just to show the possibilities. I still keep my fingers crossed that somebody from the community joins, and shares better example flows ...

Will need to have a look tonight...

I just mean that the first option sends an enormous amount of messages, even when you move the joystick not much. On the other hand option 2 and 3 only send a message when you switch from one quadrant to another. So much less traffic.

I can see your point. But I think that this would only work with option 1, i.e. when you work with vectors. But when I use option 2 and 3 I need to repeat the messages. Otherwise I don't know how many times I have to tell my onvif nodes "up", "up", ...
I think that a lot of users don't know how to work with vectors, but it is very easy for them to work with directions (up/down/left/right).
My lunch is over, so will reread your comments in detail tonight to make sure I haven't misinterpreted them in a hurry ...

... marry in haste - repent at leisure.. :slight_smile:

yes - but in case 1 - change that to repeat at a constant rate... (so no swamping and it also sends when you don't move (but not at 0,0) . and in cases 2 and 3 - they don't only send when they change - you said yourself you need it to repeat... so make it repeat at that same set rate... so only one rate needs to be set in all cases.

I think most folk can work out that if a number is > 0 move right and if < 0 move left and if 0 don't move... ... or maybe pan_x = pan_x + vector_x * some_factor is too hard ? (and indeed left and right are of course english :slight_smile:

I guess I'm also still not clear what the 45 degree angles thing is for anyway... if I move the joystick to 45 degrees the direction says
so x and y and ok - but what does angle mean ? - surely it should be up,right or something i the idea is they are a combination ?

And finally - I notice that the angle reported is actually based off 0 being to the right and then going anti-clockwise - which needs to be noted somewhere.

I'm now wondering if I should simplify the config screen, by replacing the 3 checkboxes:


With a simple dropdown containing the 3 options.

Because it makes no sense that you select multiple options at once.
And then I can also prevent that none of the 3 options is selected.

1 Like

Well my suggestion would be to not have any of them at all :slight_smile: and just always send the vector and your direction at the configured rate.

I now have this:


And one single interval for all 3 options. When this interval is 0 the messages will be triggered based on the Events option:

  • "All moves": you get the flood of messages at every move.
  • "45° degrees": you only get a message when you cross the 45° diagonals.
  • "90° degrees": you only get a message when you cross the 90° diagonals.

To be honest, I cannot press the delete button to remove those options. Probably a lot of users won't use all 3 options, but I'm pretty sure there are use cases for the latter two options ...

You really don't need a flood for option 1, just the same rate as for 2 and 3. As you don't only get a crossing message do you - you send repeats

Yes I had implemented it last night like you had suggested: a single timer for all 3 options, which indeed avoids the flood of option 1. But IF you set the interval to 0, then the above setting defines when messages are generated. So only when NO timer is active.

1 Like

I have no particular interest in joystick myself, but, I find the name of the underlying library very "interesting"... The nerd who wrote it might need some psychoanalytic attention :slight_smile:

1 Like

Sorry, of course

Guys - just please don't.


Short lunch break while working at home, so just enough time for a quick fix :wink:

To make this a bit more clear (except from the documentation), the "Events" dropdonw is now only active (and set to value "all moves") when no interval (i.e. interval with size 0) has been specified:


Hopefully this is now user-friendly enough...

1 Like

Here you go. The feeback for the joystick in construction equipment.

At least it looks impressive :yum:
Is this a possible use case for a virtual joystick like mine? Or only for professional hardware joysticks?