Funscript and vibrating toys


Forgive me if this has been covered elsewhere, I’ve looked and didn’t find anything.

Having just dived into the world of buttplug and its possibilities over the weekend, I’ve started using an XInput gamepad. I’ve done a bunch of mucking around with some videos with funscript, beat and even some pornhub json haptic files. I also messed with both ScriptPlayer and Synkydinck for playback and how creating a funscript works using JoyFunScripter to try some things out.

What I’ve noticed immediately is funscript seems easily the most available haptic file format, and it’s mostly set-up for the Launch, giving position over time.

Translating that to gamepad vibration, it becomes a super simple 100 (all the way up) = off and 0 (all the way down) = full power. This is perfectly logical, but doesn’t translate very well to what is happening in a video, except for steady, full-length strokes.

A Launch sitting statically at position 10 doesn’t stimulate much, but the vibrator would be constantly going near flat out. In comparison, the Launch rapidly bouncing from 100 to 60 should be quite intense, but the vibrator will just pulse softly.

In short, position alone does not correspond to intensity, which is what the vibrator works on. Of course, having the vibe intensity pulse with whatever stroking sort of motion is on the screen adds to the feeling as well, so that timed pulsing needs to be there too.

So finally my question. Is there some way to better correspond the position data in a funscript (or other format if applicable) to vibrator intensity? My first thought would be to look at rate of position change (i.e. Launch speed) as a more generally accurate indication for intensity.

Or is it possible other vibrating toys using different protocols would behave differently than the 100 = off to 0 = full power?

Thanks for any help, still learning a lot about how this all works.


I’ve done some experimenting with basing intensity off ‘velocity’ rather than direct position translation, but I never got results I was happy with.


I did a mess around with the same idea, using just some basic numerical methods to try and translate position from a normal funscript to speed for different data points.

Of course, the biggest issue is the funscript with positions only plots out top and bottom - where in theory the speed always hits 0.

Second thing I tried was adding mid points between all existing points in the funscript. Set all the original top and bottom points to 0 speed, and then use the slope of the line to get a speed for each midpoint.

That works okay, but there’s two problems in that it makes the up and down stroke more or less feel the same and if things are moving fast the spin up/down time on the vibration motor can’t keep pace and the pulsing, stroking sensation gets drowned out.

So far, it’s looking like just going in and editing the script to better fit a vibrating toy might be easier than trying to modify the position points according to some formula. Either that or working on how buttplug takes the funscript position data and sends it to the vibrator, but diving into that type of coding is likely beyond my current knowledge.


Funny enough, flexibility like this is one of the dreams of Buttplug. The project just hasn’t quite made it far enough along yet to realize it.

In the Buttplug message spec and in some of the old descriptions I wrote, I mention that the project is aiming to be a sort of FreePIE (or, for those of you that have been around a while, GlovePIE) for sex toys. That would address the problem that’s being discussed here, of taking funscript and turning it into vibration.

FWIW, Syncydink does a very, very basic version of this. If Syncydink get a Funscript command, but it’s supposed to be routed to a vibrating toy, I just turn it into a linear ramp that takes the duration specified in the funscript command. I don’t think that’s the best solution by any means, but even though the project is 17 months old now, I’m still aiming at Minimum Viable Product. :slight_smile:

My idea for an ideal Buttplug server interface is something that allows the user to define “filters” between messages in a way that’s… well, FreePIE-ish. For example, if you get a fleshlight command but want to route it to a vibrating toy, I’d like to have a Lua/JS/whatever other scripting language layer in the middle for people to build the filter they want (so it would go Fleshlight command -> user script -> many vibration commands or something), versus being stuck with whatever easy thing I do just to get things released. Would like to have some sort of cookbook webpage for that too, where people could share recipes.

There are two things blocking that right now. First, and most importantly, it’s super hard to gauge how many people are actually THAT interested in that level of control, which makes it hard to figure out what resources to put toward it. Outside of that, trying to figure out what tech to use for that is an issue too. I’m already fighting wars on multiple language fronts to try and keep this as cross platform as possible, so putting even more languages in the middle is daunting, to say the least.

Anyways, lemme know what you think about those ideas. It’s really great to see other people thinking about these haptics translation issues. :smiley:


Wow, great reply. I think long term your ideal is great. People who want to tinker could, people who just want to set up and go should be able to find what they need. But I’m not sure it’s a strict need, and as you mentioned certainly not the top priority.

One thing I will say is I can easily see why the funscript format made for Launch has become so popular. Mapping it to motions in a video is pretty easy (just takes time), much more so than trying to work with vibrator intensity and correlate that to speed and pressure in the video.

So while a totally customizable filter system would be great, for me the higher priority is first getting something that works (like the linear ramp in synkydink), and second tuning this initial solution into something which works reasonably well. To me customization can come later, once the default is in good shape.

Now, I’m much more a mechanics guy than software, I can code what I need to and not much more. So I am thinking of this in a more physical sense. The funscript gives us a nice triangle wave with all the maxima and minima exactly given. We also know that the Launch (and whatever is happening on screen) can’t just instantly reverse direction as the wave shows and that the reality is more of a smooth wave than the triangular one.

I’m going to stop there and muck around with some more ideas when I have the time, see if I can come up with a better translation than a linear ramp. Even if only in theory.


You may want to hop on our discord chat server at, as there are a few people there building their own machines and having to address the question of mapping things planned for the Launch to their own hardware. It’s a pretty interesting puzzle to solve (and is also somewhat blocked on me generalizing our Serial communication core in C# >.> ).

There’s some history here you may be interested in too. The Launch, as you said, is pretty simple. I wouldn’t be surprised to open up the firmware and find like, pure P (bang-bang) level controls, versus a nice PID loop. Unfortunately, those limits are kind of built into Funscript too.

Older toys like the RealTouch had FAR more movement capabilities in their communication protocols. There’s a super vague rundown of it in a library I wrote years ago:

You could basically specify ADSR style envelopes for movement within a single command, with looping capabilities built in. This was someone easier on the RealTouch as it used belts, so movement could basically be infinite in either direction.

The Launch hardware is absolutely capable of changing speeds within a stroke, but the firmware is… not good. The good news is that the firmware is distributed as a raw .hex files that can be converted back to PIC assembly, and I’ve written a new firmware loader (in javascript because why not) for the launch to put new firmware on it if need be. The bad news is that it’s using an obscure PIC24 with no decent disassembler, so rewriting it will be a bit of a chore, as we’d have to produce our own tools first. I’d love to get started on that, but managing the library itself is pretty much all of my time right now.

Anyways, definitely curious what you come with up for vibration translation, and I’m happy to help on the code side if you’ve got any questions. :slight_smile:


Maybe it’s a good idea to using an IC to control the vibration motor. Like DRV 2605. This is very popular in the vibration application.


@tonnyhu, valid point but I’m thinking most people will be using a commercial product here and not messing much or at all with hardware.

Right, last week has been nuts but I did manage to do some figuring and testing for fun as well. I’ll have to look into the discord when I have some more time. For starters here what I more or less found out.

Following is a long post of ideas, and be forewarned I don’t really know how feasible they are to execute. But maybe there will be some useful stuff in there.

Synkydink and Script Player handle things a bit differently. Script Player sends the vibrator to full power at the bottom of a stroke, while Synkydink does so at the top of a stroke. Synkydink makes repeated strokes feel much more ‘pulsey’ which is a good thing.

One peak intensity on the vibration per stroke (up and down or down and up) feels best and most like matching the screen. For hands, the most intense point at the top makes sense. For mouth or intercourse the most intense point should be at the bottom of each stroke.

For repeated, more or less steady strokes synkydink works really quite well. It’s for any other action it starts to kind of break down a bit.

Current Lacking Elements:

  1. No motion on screen causing flat vibrator output. This is common to both players - if the action stops at a position, the vibrator just stays at whatever speed that position corresponds too. So with Synkydink, if the model goes to the top and stops, the vibrator just stays on at full power.

  2. Most intense point at the top feels odd for oral or intercourse. Most intense point at the bottom feels odd for hands.

  3. Intensity generally does not correlate. Lazy, slow but long strokes on screen give a more intense vibrator output than fast, somewhat shorter ones when we should see the opposite. Again, common to both players but less pronounced in Synkydink since most fast action hits the top point (and thus full vibe power).

Possible Ideas:

  1. This exact scenario shouldn’t be too hard to fix. If the script shows no movement, vibrator goes off.

  2. Not sure there is an easy way to handle this one in all cases, since the script doesn’t tell us what is going on, just positions and times. Possible idea here is a manual ‘invert’ button so people can choose whether they want the intensity to peak at the bottom or top?

  3. The tricky one. Stroke peed is likely a good measure to use to determine intensity, and easy to get from position and time points ( [p2-p1]/[t2-t1] should work just fine). However, the speed number will really just be a relative indication, since that still needs to be translated to vibrator power. And that begs other questions such as - what is the minimum effective power of toy XYZ?

Without bogging down in specific considerations, the basic premise I have here is:
a) Figure out the speed of the stroke using a linear approximation. Just distance divided by time.
b) Use some formula to determine peak vibrator intensity for the stroke from this speed.
c) Apply this peak intensity at the correct point (bottom or top of stroke depending) and then use whatever method to ramp the vibe power from there to zero at the other end of the stroke. So every stroke would see a pattern of 0 -> peak -> 0 -> peak2 -> 0 -> and so on.

b) is the hand wavey part. I suppose we know the Launch has a maximum speed of 180 bpm, and could use that to create some sort of relation for speed to vibe power. Maybe something like 100% power for anything over 90 bpm, and just linearly decrease vibe power down to 20% for the slowest speed the Launch can go? I think others might have better ideas than me for this.

c) Once we have that peak intensity at the bottom (or top) of the stroke, vibe power can ramp from zero to that peak or that peak to zero in the same way Synkydink already does it. Sure, we could add to this and maybe use a cosine wave instead of a linear ramp (A nice quick approximation would be 100%, 85%, 50%, 15%, 0% of the peak intensity, evenly spaced over the stroke time from peak to zero) on each up or down part of the stroke for a possibly better feel, but just getting the intensity correlation working at all is more important.

A new problem:
Let’s say we get all that working. We’ve solved no movement, and strokes have some intensity which hopefully makes sense with what’s on screen. But we’ve now created a new problem - what happens if you have two, three or more up motions in a row in the funscript? Or down motions?

If a stroke doesn’t go from peak intensity to zero or zero to peak, we still need a way to deal with that. Being honest, I don’t have a good solution here. The only way I can think of doing this is just calculating a new intensity based on the speed of the stroke and ramping between this new one and the old peak. Probably linearlly, since using a cosine curve would give odd transitions at each point in the script.

What does this all look like?:
Writing that out in a general sense for position P, time T and intensity I all at point j, j+1, etc here’s the logic flow I see happening. For this example, I assume peak intensity at the bottom of strokes.

If P(j) = P(j+1), set I(t) = 0 for the entire stroke (or non-stroke really).

If P(j) < P (j+1) and P(j+1) >= P(j+2) it’s a up-down or up and stop stroke.
I(j) should already be known and is Ipeak for this stroke.
I(j+1) = 0
Ramp between j and j+1.

If P(j) > P(j+1) and P(j+1) < =P(j+2) it’s a down-up or down and stop stroke
I(j) should be given and = 0
I(j+1) = Ipeak = calculated from formula
Ramp between j and j+1

If P(j) < P(j+1) < P(j+2) it’s an up-up motion, or if P(j)> P(j+1) > P(j-1) it’s a down-down motion.
I(j) should be given.
I(j+1) = calculate as a new intensity Inew, using the Ipeak formula
Ramp between j and j+1

Final words:
Assuming that is all understandable and not just a big mess, the logic of it should give some sort of continuous vibe output with at least some correlation to what is on screen.

The big sticking points are how to figure out Ipeak from some approximate speed value, and what to actually do with up-up or down-down motions. What I have here should function, but won’t necessarily feel right.


I looked at the code for Syncydink, but I can’t really tell exactly what it’s doing that’s different from what ScriptPlayer does for vibrating devices.

That’s a lot like something I’ve been imagining. My focus is mostly on games, and it’s annoying how much stuff I basically have to smush together to get something workable. And if someone were to want to modify any of it, they have to know how to hunt down the part they want in the mess of other stuff. I can’t even switch the devices I’m using without editing some code.

My imagined system has three basic stages. A main script that receives data from a game and converts a pile of flags and animation times and whatever to a set of “something like this, to that body part” numbers on one end, endpoint scripts that take a value and send it to a device (possibly with something like a position-to-vibration filter applied), and in the middle various possible filters and transformation - selecting one of two inputs, averaging a pair, splitting an input to go to two different devices, or any number of other things. My scripting language of choice would be IronRuby though, because I actually know how to code stuff in it. (And while I appreciate cross-platformness as an ideal, I’m not really equipped to do it.)

The main script could probably also have shoved in there some non-game things, like playing scripts (either timecode based or ‘blind’ a la the lovense apps). Though honestly I’m not sure I have both the coding skills and the free time to pull any of it off.

To put it mildly. Everything I’ve read about it suggests that the RT would still be considered a stunningly versatile of equipment if it released today, never mind years ago. It’s got what, five independently controllable vectors or so? The only other thing I’ve seen that comes close is the Crescendo, but 6 vibrators isn’t going to be as expressive as vectors that do different things - the Nora demonstrates very well how effective that can be. It makes me wonder what they might have come up with for women if it weren’t for the patent trolls.

(Though I should note that I think the Crescendo could probably do a lot better with the right kind of input driving it. The official app leaves a lot to be desired there IMO.)

(Incidentally, qdot, did you see my post about the update frequency issue?)

Sometimes it bugs me how little of the architecture we’ve got supports making or using multi-vector content, but given the shortage of multi-vector devices maybe it’s not surprising. I’m hoping more will come to be with the patent expiration, but who knows.

@Errantone: I’m not sure I really understand your proposed algorithm. But I modified a copy of ScriptPlayer to have approximately what I think you’re suggesting and it seems pretty good, so I think you’ve at least got the right string.


I honestly have no idea how Syncydink and ScriptPlayer differ in handling the vibration output, other than the inverted part. I’ve tried watching the same clips using both, and the Syncydink one still feels more pulsey somehow. There’s a more distinct intensity floor and intensity peak in the vibrations, where with Script Player they feel more transient. That is of course by feel, so I could be wrong.

More recently, I’ve been taking some funscripts from RealTouchScripts to mess with using JoyFunScripter to play in Synkydink. What I would do is take the base file and invert it so vibration intensity peaks were at the bottom of the strokes. I would then go back to select any parts using hand stroking or fast motions at the tip and invert those back to top being most intense.

The results aren’t perfect, but pretty good overall. It’s not ideal having to edit the funscript and see most of the video, but better than using the unedited script.

Let me try to rephrase my proposed logic there without all the numbers and symbols. The idea is to create the vibration output between two points by considering the current point and the next two (total of 3) to have some idea what is going on. To do this in a way that feels about right, we try to aim for 0 vibration at the top of each stroke and the most intense vibration at the bottom of each stroke. The output splits into a number of cases.

Case 1 - the next position is the same as the current one. The launch doesn’t move, so the vibrator should not vibrate. Turn it off.

Case 2 (there is a logic error in my last post if the vibe is off entering this state) - The next position is higher than the current one, and the one after that is lower again. So the launch is going to go up and back down. If the vibrator is already running, it should be at the peak intensity for this stroke. Apply whatever ramp or curve down to 0 vibration at the top of the stroke. If the vibe is already stopped, need to calculate what the intensity should be and apply some curve so it ramps up to that peak and then back to 0 at the top of the stroke (this is the possibility I didn’t address before).

Case 3 - Next position is lower, then higher again, so the launch will go down and then back up. Since we’re at the top of a stroke, the vibrator will be off. Need to calculate the peak vibe output for the stroke, and apply it at the bottom of the stroke. Then add the curve or ramp to there.

Case 4 - Next position is lower, then even lower or higher than even higher. So the launch is going to move up or down, possibly with two separate speeds. We have whatever the current vibe output is, and we have the formula we are using to calculate peak vibe output for other strokes. Use that formula, shove the result on the next point and then ramp between the points.

The points to work out are - what to do to actually calculate that peak vibrator intensity for each stroke? My proposal was just to use simple Launch speed (distance/time) as a metric for how intense the vibe output should be. The trick is, that number is just some number. We would have to decide how to take that speed and turn it into a 0-100% vibe output.

Probably the easiest way would be to just block out different speed ranges and map them to specific vibe outputs, or set a minimum and maximum and put a linear ramp in between. For example, entirely off the cuff and with no real basis for the numbers I’m tossing out, you could go with something like 120 spm (strokes per minute) or higher is 100% vibe intensity. 30 spm is the minimum value at 20% vibe intensity. Connect those linearly and work it out for any other speed.

Second, how to ramp or curve between the points of vibe output after determining them. Linear ramps the way Synkydink is doing now is probably a fine enough place to start. But if we wanted to make the vibe run a little bit more smoothly and naturally, a rough emulation of a cosine wave might work. A quick way to do that would be something like this between time 0 and 1.

Time 0 - Output = Max
Time 0.25 - Output = 0.85Max
Time 0.5 - Output = 0.5
Time 0.75 - Output = 0.15*Max
Time 1 - Output = 0

Of course, you can reverse the order of the outputs for increasing intensity.

Hopefully that makes more sense? I’d be curious to see what you have Script Player doing now, though coding on this level is well beyond me.


It would be really great if ScriptPlayer could get possibility to fill gaps with no movement at all with vibes. Or lock level at selected. Now there is possibility to fill gaps with movement so adding that option there could help much. I’m super noob at coding and have been trying to make cock hero videos with .txt files which has worked for everything but breaks aka gaps. I have few vibratissimo toys but i guess that problem is same with all vibes.


@Errantone: Okay, I was doing something simpler than that. Partly because I’d missed that part, and partly because Scriptplayer doesn’t, at the point where I’m doing this conversion, have an easy way to see the position after the current destination. It knows the information for the present movement, and that’s it.

I was using a simple linear function with caps to determine the max speed, but then I noticed it’s already got a function to do this - LaunchToVorzeSpeed. I’m not entirely sold on this function for this purpose (I think it loses intensity for slow strokes too quickly, and it never caps out on high ones), but it’s a good starting point.

That leaves the low point. I had several things I wanted to factor here.
-A high speed stroke should have a stronger low point than a slow stroke covering the same range.
-A stroke with more difference between high and low points should vary more in vibration output (weaker low point) than one with the same speed covering a shorter range.
-Given an equal range, a stroke with deeper starting and ending points should have a stronger low point than one with shallower start and end points. (Compare 95->65 vs 35->5.)

Here’s what I’ve got so far. It’s messy, but I think I like the results.

        public static double LaunchToVibrator(DeviceCommandInformation information, double progress)
            const double max = 1.0;
            const double min = 0.0;
            bool negative = false;
            double speedhigh = LaunchToVorzeSpeed(information) / 100.0;
            if (speedhigh < 0.01)
                return 0;
            speedhigh = Math.Min(max, Math.Max(min, speedhigh)); //Is that function guaranteed to never be higher than 100? I don't know.
            double speedlow = (0.8 - Math.Abs(information.PositionToOriginal - information.PositionFromOriginal) / 100.0 + (1-Math.Max(information.PositionFromOriginal, information.PositionToOriginal)/100.0));
            speedlow = Math.Min(0.9, Math.Max(min, speedlow));
            speedlow = speedhigh * speedlow;
            double range = speedhigh - speedlow;
            bool increasing = (information.PositionToOriginal - information.PositionFromOriginal) < 0;
            double mprogress = progress;
            if (increasing)
                mprogress = 1.0 - progress;
            double result = range * mprogress + speedlow;
            return Math.Min(max, Math.Max(min, result));


I tried to do this as well and run into the same problem. I wonder if there is a better type of script for vibration only devices than simple timestamp txt?


(Warning, haven’t read full thread yet even though I really want to because questions like this are why I’m building buttplug, but library code maintenance is literally taking up all of my time)

It might be worth checking out how RealTouch scripts used to work. They had interested functions built into their language, so you could do curves, ramps, etc. Might be slightly more interesting for vibration that trying to predict off of fleshlight commands.

Also, don’t be afraid to come up with new formats. I’m happy to try and support them in my libraries.


Was away for a bit, catching up now.

@RemiHiyama: I did wonder if the script players even could look ahead like I had put down, but I felt it was sort of needed to fit the situation. Largely because I was trying to find those points at the top of each stroke to zero out the vibrator to try and keep a strong pulsing feel to it.

But, what you mention about logic makes sense, especially if there will be low intensity points which are not strictly off. And actually, having the low points vary in intensity might just make a lot of sense.

If the vibrator only goes to full power at the single most intense point in a video, then exactly like you’ve seen it won’t every use that full power elsewhere (or at all). At a certain point, I think the vibe probably just needs to go full power at the peaks. Varying the intensity of the lows can then distinguish the feel of different high intensity inputs.

As for how to do it, I’m not sure. A really simple way is just to throw in a multiplying fudge factor. So instead of using LaunchtoVorzeSpeed directly, use XLaunchtoVorzeSpeed. Figuring out X would be a matter of feel. The only other thing it would require then is a statement so that if XLaunchtoVorzeSpeed > Allowed Maximum Value, just set the result equal to the maximum.

@qdot: I might have to try to find some time to look into those, or the scripting in general. As for new formats, that might be a good possibility as well. On the other hand, it would be ideal to have something working with the most common formats people are actually using. Something to think about.