That’s pretty interesting, but i think we would need some kind of live preview of what it is generating so people are able to properly switch between them during more precise timings or specific sections, otherwise it can be a bit of guesswork.
SaekoM
August 14, 2026, 10:53am
22
Well it was never really intended to switch mid preview…
Ideally you only preview one script at a time.
But maybe I can just make it a feature?
It’s fine mate!
It’s more of an niche feature, i would say most people won’t have an use for it, but it’s up to you
1 Like
has it been integrated into this fork yet? or still only SaekoM ’s fork?
SaekoM
September 3, 2026, 10:49am
25
Still only a fork at the moment. I can get a PR ready
master ← SaekoM:master
opened 12:38PM - 03 Sep 26 UTC
Two larger features from my fork ([SaekoM/OFS](https://github.com/SaekoM/OFS)), … plus a few smaller additions that grew out of them. Everything is opt-in: no window opens, no shortcut fires and no color changes unless the user turns it on, so existing behaviour and existing configs are untouched.
### 3D multi-axis simulator
`View > 3D Simulator`. Renders the loaded scripts on a 3D device instead of the flat bar, so multi-axis work can be checked without loading the project into a separate simulator.
- Drives stroke + surge / sway / twist / roll / pitch from the axis scripts, using the TCode channel-suffix convention already used for multi-axis loading.
- Mesh and default motion ranges match the widely used reference simulator (OpenFunscripter/OFS_Simulator3D): stroker cylinder with opposing twist markers, optional tongue and center indicator, twist ±120°, roll/pitch ±45°. Rotation order is Rz(roll) · Rx(pitch) · Ry(twist) with twist applied locally, so the model spins about its own shaft rather than precessing once roll/pitch are engaged.
- Device presets — All axes / SR6 / OSR2+ — restrict the driven axes to what the hardware actually has, plus per-axis invert and range sliders.
- Orbit camera with recenter / reset-ranges, ground grid, axis gizmo, per-part colors, and an optional stroke-position readout (0–100) drawn under the model.
- Two draw paths: a fast ImGui draw-list path (default) and a GPU-lit render-to-texture path.
- Overlay mode puts the simulator in a borderless, movable/resizable window over the video.
### Animated preview export
`View > Preview Exporter`, or right-click a chapter → Export preview. Produces a short shareable clip of a script instead of a still heatmap.
- Output: animated WebP (lossless) or AV1 in mp4 (libsvtav1, selectable preset), at a chosen height and fps, from a chapter or a typed HH:MM:SS.mmm range.
- Optionally composites the simulator onto the video — the 3D device or a flat 2D stroke bar that reuses the user's existing 2D-simulator colors, height lines and indicators — placed with a draggable live-preview box.
- Optionally appends a scrolling timeline strip beneath the video, drawn with the editor's own BaseOverlay code (OFS_TimelineRasterizer) so the exported strip matches what you script against; supports stacked lanes with axis labels for multi-axis projects.
- Compilation mode samples N evenly spread slices of a chosen length across the whole video and concatenates them into one preview. AV1 compilations concatenate audio too, falling back to video-only when the source has no audio track (via a new OFS_Videoplayer::HasAudio(), backed by an mpv aid property observation).
- Overlay frames render incrementally on the main thread (a few per app frame) so the UI never blocks; ffmpeg then runs once on a background thread with stderr captured and logged.
### Selectable action-line color profiles
`Preferences > Scripting` gets an action-line color picker: the current Fork palette (the detailed 41-stop speed gradient, unchanged default), the original OFS Classic palette (white → green → yellow → red), or a Custom gradient edited stop by stop with a live preview. FunscriptHeatmap gains LinePreset() / SetLineColors() so the gradient can be rebuilt at runtime.
The Classic preset compresses its stops into the first 400/2000 of the speed axis, because the original normalized speed to 400 where this normalizes to 2000 — without that the stops land on the pale low end and look washed out. The compressed mapping is numerically identical to the original (fully red at ≥ 400/s). Only the timeline action lines are affected; the exported heatmap image keeps the existing shader palette.
### Smaller changes
- Shift + 1…0 keybinds for the intermediate positions 5, 15, … 95 (with localization rows).
- `Project > Add > Shortcuts > All` (surge, sway, twist, roll, pitch) creates the five positional axes in one click, skipping any already loaded.
- Fixes an ImGui "empty ID at the root of a window" assert when opening File > Recent files: an entry with an empty name produced an empty MenuItem ID. Each entry now seeds its ID from its index and falls back to (unnamed).
- Lua API tolerance — Checkbox accepts a missing/nil value, and SliderInt / InputInt / DragInt take lua_Number and round, so existing community extensions stop erroring on int/float marshalling.
- OFS_DynFontAtlas::NumberFont: a digits-only 96px RobotoMono face, rebuilt with the other fonts. The simulator readout scales down from it instead of upscaling the UI font, which stayed soft. Limited to 0-9 so the atlas cost stays negligible.
- Build: set(CMAKE_POLICY_VERSION_MINIMUM 3.5) at the top of the root CMakeLists.txt so the bundled submodules (glm, SDL2, json, bitsery, civetweb, eventpp), which still declare cmake_minimum_required() below 3.5, configure under CMake 4.x without patching each one.
- The bundled ffmpeg download switches from the gyan essentials build to full, which is the only one carrying libsvtav1 (needed for AV1 export). Users with a cached ffmpeg.exe / ffmpeg.zip in the OFS pref dir need to delete them to re-pull.
1 Like
Sounds good, but there is an issue with the appimage anyways, MPV pops out of the main window twice and it locks up all but the exit button