I have connected the handy via bluetooth to the handy app and used the experimental control feauture, with various patterns. These patterns have very good quality as if it was on WiFi. Why is it that bluetooth performs so good, but when using funscripts its not as smooth? Both use Bluetooth…
It has to do with how instructions are delivered. There’s a bit more steps between delivering instructions from funscript and less from the control. The added steps and distance of the “wire” makes the latency much higher. It’s the same problem as audio on your car. If you have a slightly older car, you most likely notice the audio is a bit delayed.
It’s also known that bluetooth isn’t a great protocol to begin with.
But the app also uses bluetooth to communicate with the handy…
When you use the app to control the handy your pipeline is:
App > instruction > handy
When you use funscripts the pipeline is:
App > Realtime instruction conversion > instruction > handy
The small amount of time it takes for the app to convert funscript instructions to direct instructions is slow enough that it causes that problem.
This is oversimplifying it but that’s the core problem.
Thanks for your response! ![]()
I am still wondering why it would be smooth when the app delivers it vs other bluetooth interfaces, Why can this not be achieved using a pc and bluetooth, Is it the way the instructions are delivered?
Is there a way to deliver the instructions directly as the app does, maybe preconversion before streaming it to the handy?
You’re not wrong about this but there comes a tradeoff. If you pre-calculate the full script, you have to wait for that. If the script is long that can be a lot longer than someone wants to wait.
Also what if mid-transmission your handy disconnects. You’ll have to re-calc the script again.
It’s always a developers decision to implement what loading strategy exists for a piece of content.
It’s like in web browsing. Do you want to wait for everything to load before you an interact with the page? or load what is immediately visible and start interactivity sooner.
Videogames do the same thing for optimisation. Culling elements off screen to save framegeneration processing time. Do you load the entire level into memory or have a treadmill and no loading screens.
Hopefully that makes it a bit clearer.
Game optimization pioneering devs are so impressive for the methods they came up with to get huge games working on the hardware for that time.
GTA3 ran on 32MB of RAM, that’s baffling for me to think about nowadays.
As a developer I’m having a really hard time believing that any instruction conversion adds more than maybe 1ms max of latency. Funscripts are an extremely simple format and you have the benefit of knowing the entire script ahead of time. The amount of data involved is significantly less than any realtime audio application.
The only way this would make sense to me is if the implementation is either incompetent or intentionally hamstrung to push their cloud products.
Do you have to connect the player to the app, and the app to the handy? Is this what’s adding extra latency?
i am very surprised that developers not tried to convert this in advance to achieve the same high quality results the app can achieve via bluetooth. Is there any documentation available to try this?
Yes, I am starting to think more in that way as well, that this is to push cloud products, hopefully it gets more open sourced and possible to run own server or somehow to the same thing the app can do. I assume there is some gap in what devs can do and what the app is able to…
@ironmortar is right — parsing a funscript costs nothing, that’s not where the time goes. The difference is which protocol the client is driving.
HDSP sends one target at a time — “go to this position at this speed.” The device has no idea what comes next, so every stroke depends on the next command showing up exactly on time, and any hiccup on the host or the link lands directly in the motion. It’s a command protocol, not a playback protocol. It’s great for live/interactive control; it was never meant to play a script.
HSP works the other way around. The client pushes timestamped points into a buffer on the device ahead of time, and the device plays them against a synced clock. It already knows where it’s going next, so a packet arriving slightly late doesn’t turn into a late stroke. That’s what the experimental control in the app is driving, and that’s why it feels the way it does.
HSP is available over the BLE RPC too — it isn’t WiFi-only and it isn’t gated. The BLE connection is a full RPC connection; you can drive the device in a lot of ways. It’s just more work: you have to keep the buffer fed and keep the clock in sync. HSSP is the API layer we put on top of HSP to do exactly that for you — you upload a script and the server handles the rest. That’s the difference in effort, not in capability.
On why we put more into WiFi + API than into BLE:
- Bluetooth behaves differently on every host. A REST API just works “everywhere”.
- User journey. BLE, legacy BLE, our server, custom servers — multiple routes are confusing for most people. We’re trying to make one route good while still supporting the others (which is not an easy balance…)
- We’re adding more servers this year. The goal is WiFi latency indistinguishable from BLE.
And on the cloud-lock-in point: custom server endpoints exist. We haven’t published anything about them yet — there’s still some tinkering to do before we finalise it — but they’re built, and the intent is that you can point a device at your own server instead of ours.
So the app generates patterns, pushes them to the device via HSP, then begins playback? This would only incur any transport latency once whenever the user changes the pattern, rather than for each individual point. It’s similar to what I had in mind for improving upon TCode in DIY strokers.
Meaning @Carrot’s issue is just that no player properly uses this API?
I’m in the middle of developing a MVP for a desktop cross-platform, multi-axis, multi-device funscript player with built-in filtering. The architecture lazily sends chunks of about ~500 points through a chain of filters (invert, copy and mix axes, interpolate, decimate, add vibration, etc.) and to connected devices.
I will likely be looking at integrating handy support at some point, and it seems like the HSP API would be a good fit. Is there a way for developers to test their integration without a unit on hand?
Thank you for the explanation and clarifications. I was just hoping this would spark a discussion and finding a way to improve funscripts ![]()