Context
I’ve been interested in automatic funscript generators, but I kept running into the same problems. Some are too slow, some sometimes detect nothing at all, some need too many steps, some are paid, some hallucinate actions, and some need constant human supervision.
I’m studying for an engineering degree while working, so I have no time to script or build scripting apps. Instead, I designed an algorithm that is optimized to make the best script possible with the requirements in mind, then vibe coded it until I had a working app. It’s free and open source of course.
Requirements
- High processing speed, which means no neural networks or ML models
- Works with different kinds of scenes (insertion, HJ, BJ, fingering, dance)
- Runs completely unattended and produces a usable script
- Available as a command line tool and as a GUI
- CPU only, no GPU needed. The only dependencies are numpy, scipy, OpenCV and ffmpeg, plus DearPyGui for the GUI
- It can’t rely on object detection, so it works on every kind of video that has oscillating motion like a cooking video. It detects movement and it scripts it, the question is if it’s the right thing to track or not
What I’m willing to give up on
Every approach has downsides, and this one has some.
- Orientation accuracy: sometimes the script comes out inverted. To keep it consistent, the 100 position points up by default, and the 100 of every stroke in a chapter follows that guide. You can select a chapter and press i to invert it.
- Positional accuracy: velocity matching is the priority, position comes from integrating the velocity so its not perfect.
- Unrelated motion: it can track movement that isn’t the activity, especially periodic motion stronger than the action you want. When the main motion pauses, the automatic region may follow whatever moves next.
- Camera motion: it isn’t compensated.
- Single tracking point: It can’t track 2 bodies at the same time.
How it works
- Decode and downscale. ffmpeg outputs greyscale frames at low resolution (192 px wide, 30 fps by default).
- Optical flow. Dense Farneback flow is computed on every frame pair, then averaged into a grid of cells (12 px by default). This is the only slow step, so it’s cached to disk.
- Scene cut detection. Cuts are detected from frame difference spikes. Region choice, direction and normalisation all reset at each cut.
- Region of interest. In a sliding window (3 s by default), candidate regions are ranked by motion intensity and periodicity (the autocorrelation peak of their motion). The winner only replaces the current region if it beats it by a margin, which avoids jitter.
- Direction and velocity. The dominant motion axis comes from a magnitude-weighted PCA on the flow vectors. The velocity is measured along that axis.
- Integration and filtering. Velocity is integrated into position and then passed through a zero-phase Butterworth bandpass (0.05 to 6 Hz by default) to remove drift and noise.
- Normalisation and enhancement. The signal is scaled to 0 to 100 with a rolling percentile window (30 s by default). Optional enhancements like smoothing, gain, boost, offset and a second pass for small fast strokes are available.
- Keyframes. Peaks and valleys are extracted with minimum amplitude and interval constraints, and a speed limit (400 units/s by default) protects the device.
TL;DR: the computer chooses the best place to track, then tracks it.
Features
- Automatic chapter detection (hard cuts, fades, axis changes and zone changes, all optional)
- Chapters are saved next to the video in a
.chapters.jsonfile and restored automatically - Per-chapter manual ROI, zoom and invert
- Hysteresis switching of the region of interest
- Configurable low-pass and high-pass filters, enhancement and normalisation
- Zoom option that crops the motion zone before downsampling, which helps with subtle motion.
- Second-pass enhancement for small fast strokes hidden inside a larger slow oscillation.
- 3D support for SBS and TB videos, plus a VR mode that zooms in to remove edge distortion (proper reprojection would be better)
- Optional GPU acceleration (OpenCL), multithreading by splitting the video into chunks, and batch processing of several files at once
- Resumable batches: videos that already have a script are skipped
Everything is configurable through command parameters or GUI sliders. Because optical flow is cached, you can change any parameter that doesn’t affect the flow and fine-tune the funscript instantly, without recalculating. This also works in bulk: after a batch, changing something like gain or smoothing regenerates every cached video almost instantly.
Performance
It processes 200+ fps on my PC, sometimes 500, parallel extraction makes it double the speed for me. 500 fps processing speed is insane and with the video at 30fps imagine the time it takes for a video to be scripted,and the quality is pretty good at least in my experience, it’s not perfect but the best approach I have seen to date.
Tools used
- Video processing: ffmpeg, OpenCV
- Numerical processing: SciPy, NumPy
- Graphical interface: DearPyGui
Demo
All demos were made completely unattended. The results are exactly as the program produced them, with no human intervention. I also still don’t know what are the best parameters
GUI
The interface is split into windows, each with its own purpose:
- Control: process videos or trigger actions.
- Video: the video with overlays for optical flow, region of interest, candidates, window averages, magnitude, frame magnitude, periodicity, and the measurement axis with live funscript arrows.
- Video Control: toggle overlays, set manual zoom or manual region of interest.
- Parameters: optical flow processing and post-processing settings.
- Overview: the timeline, color coded by direction, position, pace, velocity, amplitude, etc…
- Signals: the optical flow signal projected onto the measurement vector, and the funscript itself.
- Folder Videos: see which videos are already scripted and select videos to process.
- Status: detailed processing progress.
It uses DearPyGui, so the look is similar to OpenFunScripter. There are 17 color themes.
Video examples





Download
Builds for Windows and Linux are on the GitHub releases page. You can also run it from source or use Docker.
Repo:
Releases page:
Download the funautoscripter_gui for the graphical user interface
It’s 100% vibe coded. The AI also wrote a document explaining all the math, its in the repository, and a guide with placeholder images, which I’ll replace eventually.
This software is released under MIT License.
I created this for personal use but thought that it would be great if i shared it with the community. Eventually i should release some updates even though i am very busy.
Enjoy!
Changelog
v1.7.x – v1.9.x
- v1.7.0 - 2026.10.25 - Dual axis, pan/zoom timeline, batch chapter detection, cheat sheet, Process button
- v1.7.1 - 2026.10.25 - Fix file picker
.*filter for single video import, fix timeline axis switch - v1.7.2 - 2026.10.25 - Axis 2 independent signal settings
- v1.7.3 - 2026.10.25 - Fix timeline axis selection reset on parameter change, restore Axis 2 header visibility on startup
- v1.8.0 - 2026.10.25 - Per-chapter axis mode, overview axis selector, individual parameter reset buttons, Ctrl+arrow precision nudge, Axis 2 signal defaults
- v1.9.0 - 2026.10.25 - Exclude chapter from Save
- v1.9.1 - 2026.10.25 - Bundled default layout: first run and reset now restore the correct docked layout
v2.0.0 – v2.0.x
- v2.0.0 - 2026.10.26 - Axis lock (resist tracking deviations from chapter axis-ref for consistency on multi-axis), remove y axis mode (use angle 90°), fix direction resolution applying to both PCA and forced-angle modes
- v2.0.1 - 2026.10.26 - Fix folder open path duplication (DPG bug on all platforms)
- v2.0.2 - 2026.10.26 - Debug layout copy, move create_context after ini copy, add _MEIPASS fallback
- v2.0.3 - 2026.10.26 - Fix default_layout.ini missing from CI build (–add-data not in workflow), fix reset layout overwritten by ImGui auto-save
- v2.0.4 - 2026.10.26 - Reset layout closes app immediately
- v2.0.5 - 2026.10.26 - Fix reset layout: delete layout.ini after destroy_context so ImGui cannot recreate it
