OSR2 & SR6 Funscript Setup: Multi-Axis Playback Without the Headache

A practical guide to multi-axis funscript playback on the OSR2 and SR6: how T-Code axes work, per-axis file naming and mapping, range and speed limits that protect your servos, and how to generate multi-axis scripts with HaptiQ.

Building an OSR2 or SR6 is the fun part. Getting it to actually play a funscript — let alone a full multi-axis set with twist, roll, and pitch moving together — is where most builders get stuck. The information exists, but it's scattered across the OSR wiki, the T-Code spec on GitHub, and years of forum threads that assume you already know what L0 means.

This guide pulls it into one place: what the T-Code axes are, how multi-axis funscript files are named and mapped, which limits to set before you run your first script, and how to generate the scripts themselves — including with HaptiQ's multi-axis support.

OSR2 vs SR6: what you're actually driving

Both devices are open-source, 3D-printed stroker robots from the same design lineage, and both speak the same protocol. The difference is degrees of freedom:

The important takeaway for setup: an SR6 is not a different protocol, just more axes. Everything below applies to both; the SR6 simply uses more of it.

T-Code in five minutes

T-Code is the serial command language these devices run (typically on an ESP32 flashed with the open-source T-Code firmware). Each command names an axis and a target position on a normalized scale, optionally with timing information, and the firmware turns that into servo movement. You'll rarely type T-Code by hand — your player software generates it — but you do need to know the axis map, because every mapping screen and every multi-axis funscript filename refers to it:

An OSR2 uses L0 and R2 (plus R1 on an OSR2+). An SR6 can use L0, L1, L2, R0, R1, and R2. HaptiQ's multi-axis engine supports all eight of these axes, so scripts you produce there map one-to-one onto the T-Code axis list.

Multi-axis funscripts: one file per axis

There is no single "multi-axis funscript" file format in common use. Instead, the community convention is one .funscript file per axis, grouped by filename. The main stroke script keeps the plain name, and secondary axes add a suffix:

Drop all of them in the same folder as the video, and a multi-axis-aware player will pick them up automatically and assign each one to its axis. If a script for an axis is missing, that axis simply stays at its neutral position — a plain single-axis script plays fine on an SR6; it just only drives the stroke.

The playback chain, step by step

Here's the path from files on disk to a moving device:

  1. Verify the firmware and connection. Confirm your ESP32 responds over its serial (USB) connection at the baud rate your firmware expects. Most player software has a "connect" or test function that will move the device to neutral when the link is good. Fix this first — nothing downstream matters until the board answers.
  2. Pick your player/bridge. For OSR2/SR6 owners the common choice is a dedicated T-Code player such as MultiFunPlayer, which loads multiple funscripts, merges them, and streams T-Code to the serial port while syncing to a video player. The Buttplug.io ecosystem (via Intiface Central) also supports T-Code serial devices, which is useful if the rest of your setup already runs through it — HaptiQ's web app, for example, talks to Buttplug.io v3 over a raw WebSocket, no extra npm-style plumbing involved.
  3. Load the script set. Point the player at your video; it should auto-match the .funscript files by name. Check the axis assignment screen and confirm each suffix landed on the axis you expect.
  4. Set your limits (next section) before pressing play for the first time.
  5. Play and watch the first minute at reduced range. Listen for servo buzzing or stalling, then open the range up gradually.

Per-axis mapping and range limits

Every decent T-Code player lets you configure each axis independently, and this is where you protect both the hardware and the experience:

Speed limits: protect your servos

Hobby servos have a real maximum speed, and a script point pair that demands a full-range move in a few tens of milliseconds will make the servo stall, buzz, overheat, or skip. Auto-generated and heat-of-the-moment hand-made scripts both produce these spikes.

Two layers of defense:

Where HaptiQ fits in

The hardest part of multi-axis playback isn't the plumbing — it's getting scripts worth playing. Hand-scripting one axis is slow; hand-scripting six is a serious time commitment.

HaptiQ approaches this from the generation side. It's an AI funscript generator: upload a VR or 2D POV video and a 5-stage pipeline (frame extraction, optical-flow motion detection, region-of-interest analysis, funscript generation, post-processing) produces the stroke script automatically, with live per-stage progress in the browser. From there, the multi-axis tools take over: HaptiQ supports all 8 T-Code axes (L0–L2, R0–R2, V0, A0), can derive secondary-axis motion from the primary stroke so your OSR2+ or SR6 has something to do on roll and pitch without manual scripting, and lets you refine any axis in the timeline editor with 20+ keyboard shortcuts and the transform plugins mentioned above. The output is standard .funscript, so it drops straight into the filename-suffix workflow and works with the players you already use.

FAQ

Do I need a separate funscript file for every axis?

Yes, by convention. Multi-axis playback uses one .funscript per axis, matched by filename suffix (.roll, .pitch, .twist, .surge, .sway) next to the main stroke script. Players merge them at playback time. Any axis without a file just stays neutral.

Can I play a normal single-axis funscript on an SR6?

Absolutely — it will drive L0 (stroke) and leave the other axes parked. If you want the extra axes moving, you can hand-script them, or use a tool like HaptiQ to derive secondary-axis motion from the stroke script.

Why does my OSR2/SR6 stutter or buzz on some scripts?

Usually the script is demanding faster position changes than the servos can physically make, which causes stalling and buzzing. Run the script through a speed limiter and smoothing pass, and set per-axis range limits in your player. If it happens on every script, check your serial connection and power supply before blaming the files.

What's the difference between T-Code and funscript?

Funscript is the file format: timestamped position points stored as JSON. T-Code is the live command protocol your player streams to the device over serial during playback. The player reads funscripts and emits T-Code — you need both, but you only ever author funscripts.

Does HaptiQ connect directly to my OSR2 or SR6?

HaptiQ's job is generating and editing the scripts, including full 8-axis multi-axis sets. For playback, T-Code serial devices are typically driven through the Buttplug.io/Intiface Central chain (which HaptiQ's web app supports) or a dedicated T-Code player like MultiFunPlayer using the exported .funscript files.

Try HaptiQ

If you'd rather spend your time tuning your build than hand-placing script points, try HaptiQ free at haptiq.app — upload a video, get a .funscript, and use the multi-axis tools to put every axis of your OSR2 or SR6 to work.

Founding members can lock in lifetime Pro for a one-time $99 (normally $199.99), limited to the first 100 members — details on the pricing page.

Frequently asked questions

Do I need a separate funscript file for every axis?

Yes, by convention. Multi-axis playback uses one .funscript per axis, matched by filename suffix (.roll, .pitch, .twist, .surge, .sway) next to the main stroke script. Players merge them at playback time, and any axis without a file stays at its neutral position.

Can I play a normal single-axis funscript on an SR6?

Yes — it will drive L0 (stroke) and leave the other axes parked. If you want the extra axes moving, you can hand-script them or use a tool like HaptiQ to derive secondary-axis motion from the primary stroke script.

Why does my OSR2 or SR6 stutter or buzz on some scripts?

Usually the script demands faster position changes than the servos can physically make, causing stalling and buzzing. Run the script through a speed limiter and smoothing pass, set per-axis range limits in your player, and if it happens on every script, check your serial connection and power supply.

What's the difference between T-Code and funscript?

Funscript is the file format — timestamped position points stored as JSON. T-Code is the live command protocol your player streams to the device over serial during playback. The player reads funscripts and emits T-Code.

Does HaptiQ connect directly to my OSR2 or SR6?

HaptiQ generates and edits the scripts, including full 8-axis multi-axis sets. For playback, T-Code serial devices are typically driven through the Buttplug.io/Intiface Central chain (which HaptiQ's web app supports) or a dedicated T-Code player using the exported .funscript files.

Create a free HaptiQ account to generate a funscript from your own video.

Open in HaptiQ