PythonDancer - A port of FunscriptDancer to Python

i just played around with the PythonDancer but i still dont get the exact meaning of target speed, pitch and %. Can someone explain this to me?

Also is it possible to make slower scripts with this? looks like that every generated script will have the same amount of strokes per second

1 Like

Hey everyone!
This guide breaks down what all the buttons and sliders in the PythonDancer interface actually do.

:warning::police_car_light: Just a heads-up: I used an AI (Gemini) to help figure this stuff out based on the program’s GitHub notes, so don’t take it as gospel. Think of it as a starter guide—I used it myself to learn the basics, so it should give you a solid idea of how everything works!

1. Manual Analysis Controls

These two controls define the baseline for the script generation. They allow for precise manual tuning but are adjusted automatically when the Automap feature is active.

  • Pitch → Offset
    • Function: Sets the Tone Baseline. It defines the neutral or starting position for pitch-based movements. This sets the threshold for determining if the audio tone is “high” or “low” relative to this baseline.
  • Energy → magnitude
    • Function: Sets the Intensity Threshold. This defines the level of audio energy (volume) that the music must reach to generate the maximum/strongest movement in the script.

2. Automap Settings (The Easiest Way to Calibrate)

If the Automap checkbox is selected, these settings automatically calibrate the manual controls (Pitch Offset and Energy Magnitude) based on simple targets.

  • Target Pitch
    • Function: Reference Tone Threshold. This value automatically adjusts the Pitch → Offset control. Set this to the average pitch line observed in the audio graph to establish the script’s core tonal reference.
  • Target Speed
    • Function: The Maximum Speed Limit (Hardware Limit). This is the highest velocity value (e.g., 500) the program will write into the motion file (.funscript).
    • Purpose: Acts as a safety cap for your hardware. Set this to your device’s maximum speed.
  • Target % (Active Movement Ratio)
    • Function: Controls the percentage of time you want your script to contain high action. This value automatically adjusts the Energy → magnitude control.
    • Developer Context: This feature was introduced in the “New automode” update to control the number of movements generated that are “above the set percentage.”
    • Logic: When you set Target % (e.g., 75), the program finds the lowest energy level necessary to ensure that 75% of the song is considered “active.” It uses the inverse percentile (25th percentile) to set the threshold, ensuring the resulting script meets the desired action ratio.

3. Out of Range Options

These options determine how the program handles calculated motion values that exceed the Target Speed maximum.

  • Crop
    • Effect: Capping. The excessive value is simply limited directly at the Target Speed (e.g., 500). Use for a smooth and safe script.
  • Bounce
    • Effect: Reflection. The excessive value is treated as a reflection, causing the movement to “bounce” back into the acceptable range. Use for an intense, dynamic script.
  • Fold
    • Effect: Compression/Wrapping. The excessive value is mathematically “folded” or wrapped back into the acceptable range. Use for a compact, controlled action.

4. Misc Options (Miscellaneous Tools)

  • Automap
    • Function: Automation Toggle. Activates and deactivates the Automap Settings section.
  • PLP estimation
    • Function: Rhythm Correction. Uses the advanced PLP (Perceptual Linear Prediction) algorithm to stabilize beat detection.
    • Developer Context: This tool was added to prevent beat “drift” over long tracks. Highly recommended for longer audio/video files.
  • Heatmap
    • Function: Visual Output. When checked, this allows the user to export a visual intensity map of the generated script for review.
5 Likes

Hey, this program has been working well for me so far. I’m wondering if it’s possible to port the code as a browser plugin so that it can generate a funscript of whatever video/music the website is currently playing and possibly sync it up live. If anyone knows of an existing program that does this or have any idea on how to do so, I would love to hear your suggestions.

A real-time version would be a completely different approach unfortunately. Currently the program extracts all the audio and looks at the entire thing to generate a script.

feelme.com does video based toy control in real time via browser plug-in. I know there’s at least one other site doing the same, but I forget the name… the word “sam” comes to mind though.

Interesting, have you personally used the website you mentioned and if so, how long does it take to generate a funscript for a standard 5-10 minute video? Also how accurate is it for different videos including ones where scenes constantly change like PMVs or HMVs?

Also, yeah it would be tricky to generate an audio based script in real-time as the video is playing but I was thinking more like a plugin with the ability to download the video, run the funscript generation, and then sync the generated script to the video all in the browser. There’s several software available now that can do the last part of fetching and syncing funscripts to videos so it’s just a matter of automating funscript generation.

To clarify - feelme.com offers a browser plugin that analyzes the video as it plays and moves the toy accordingly. It doesn’t generate a funscript at all. I have used it, but I usually watch my porn in VR so it hasn’t been as useful as I’d hoped.

Good job! Documentation is the bane of every dev.

I should really move this to typescript so i can use it everywhere, maybe even integrate it into my stash heresphere integration app.
Unfortunately i dont know any libraries that do audio in typescript where i dont have to reinvent audio processing.
Maybe i’ll guide an ai to do it.

I forgot i already had an ai do most of it:

Heavily guided towards better solutions ofc, and some broken things are disabled (and some things are just downright broken)

4 Likes

Interesting idea to move it to TypeScript and integrate it into other projects, and also the web version :ok_hand:. It would be great to someday see a single, unified program that includes everything needed to create any kind of script. :drooling_face:

I just wanted to share my personal point of view regarding OpenFunscripter original VS the Python version. I know many people prefer the Python approach and several have told me it gives them better results, but after trying both versions, I personally still prefer the behavior of the original FunscriptDancer.

For me, the main difference is not so much the overall result (which in many aspects is quite similar), but how the movement is distributed. In the Python-style version, the script seems to always share the same center, and the movement tends to remain very centered around a single axis, even when adjusting parameters like pitch. This makes the result feel more uniform, with less variation.

In contrast, in the original version you have more freedom to decide whether you want a more centered script

or a looser, more dynamic one, with more natural variations in amplitude.

Other than that, both solutions seem very similar and perfectly valid to me; it’s just that, for my personal taste, that extra control over the center and movement range makes me prefer the original version.
I don’t know if it would be possible to program the Python version to behave the same way, although honestly, training an AI to create PMVs would be crazy :melting_face: (in a good way) it could probably understand rhythms much better than a simple algorithm (speaking from my ignorance).

2 Likes

Im now more faithful to the original in the js version, so you can now have ur squiggly lines, or dont by keeping pitch offset and amplitude centering at 0.

tried Releases · NodudeWasTaken/PythonDancer · GitHub
but anything I add it says something like “failed to convert to wav” even after providing a .wav file

tried pythondancer-js with the converted file
but it says “Maximum call stack size exceeded”

The only time I’ve seen “failed to convert to wav” is when the file I provided had multiple audio tracks in the video.

The WAV file is extracted to the Python script’s directory in a tmp subdirectory. If you put the Python script in “Program Files” it may lack the permissions to create tmp or write to it. If you’re on another platform it may be a similar permissions issue.

Apparently “Maximum call stack size exceeded” can happen if you use Math.max on really large lists in JS, its fixed now.
Do note that its dog slow to use the web version on 1 hour+ scenes, and chrome apparently has a 2gb limit on video files.

Make sure to download and place ffmpeg.exe in the same directory as FunscriptDancer.exe
Took me hours to figure that out in the beginning, too, since neither the readme.md on GitHub nor the OP right here mention it. It’s well hidden somewhere in the comments and in the release notes.

3 Likes

this was, thanks

can I have a funscript editor recomendation? I like the output but I would like to modify the lenght of the stroke or shorten it on some parts of the video
or double/halve the tempo on some, maybe even create a different beat and replicate it while fitting the song tempo

are you only updating javascript version? any way to make it work offline?
I see some features missing from .exe version like target speed, but I like the pitch offset from the js too

I get the distinct feeling you want me to update the python version.
It is done!
Pitch offset now actually adjusts the pitch offset instead of centering.
So you now also have a dedicated centering bar.

This means you can make ur script “path” more squiggily.
Also sliders are now live updating (something i missed when changing from qt to tkinter).

It will be up on github soon™

5 Likes

thank you!
do you have any recomended addons for OFS?

Thanks for the new features! Big downside is now I want to go re-generate hundreds of funscripts.

One regression I noticed - previously the sliders could be fine adjusted using the arrow keys on the keyboard. This no longer works with the new version.