Skip to content

Add support for joypad haptics (force feedback)#114642

Draft
Nintorch wants to merge 1 commit intogodotengine:masterfrom
Nintorch:steering-wheels-force-feedback
Draft

Add support for joypad haptics (force feedback)#114642
Nintorch wants to merge 1 commit intogodotengine:masterfrom
Nintorch:steering-wheels-force-feedback

Conversation

@Nintorch
Copy link
Contributor

@Nintorch Nintorch commented Jan 6, 2026

Closes godotengine/godot-proposals#8309

This PR exposes SDL's Haptic subsystem to the user.
(P.S. Personally, I don't know how to use this subsystem, but other people do, so it would be useful to them 😅)

Made possible by my small GodotBoilerplateGenerator :D

TODO:

  • What is an offset parameter mentioned in the original proposal exactly?
  • Is this approach good enough or should we expose the entirety of the haptic subsystem? (With InputHapticEffect classes, constants and such, which sounds quite tedious to both use and implement 😅)
  • Other effect types?
  • Finish class boilerplate
  • Replace M_PI with Math::PI
  • Input.create_joy_haptic_effect()
  • Input.start_joy_haptic_effect()
  • Input.update_joy_haptic_effect()
  • Input.stop_joy_haptic_effect()
  • Input.remove_joy_haptic_effect()
  • InputHapticEffect.direction_degrees
  • Make the code prettier
  • Better default values for classes to make the system easier to use
  • Documentation
  • Mark as experimental

@berarma
Copy link
Contributor

berarma commented Jan 6, 2026

What is an offset parameter mentioned in the original proposal exactly?

It's used in conditional effects (named center in SDL3) together with the deadband parameter to set a dead zone for this kind of effects. Wheels in real vehicles have a dead zone where the wheel is resting when driving straight. These parameters can simulate that dead zone, and when changing the offset it can also simulate a broken steering that has its center off.

It's also used in periodic effects to the set the mean value.

Is this approach good enough or should we expose the entirety of the haptic subsystem? Other effect types?

The periodic effects should be included, they can be used to simulate vibrations on the wheel without having to update the effects periodically.

We're missing parameters like direction, saturation, deadband, envelope, repetition, etc.

I like better the SDL3 approach with generic functions for all effect types, and passing the effect as a parameter. It makes it easier to add many effects without duplicating code. It also has structs with all parameters that are standard nowadays for force-feedback.

It's probably tedious to use, but it gives way more control over the effects, kinda professional versus a hobbyist implementation. I appreciate its simplicity but it would turn fast into excessive rigidity when using it seriously.

It might be a bit tedious to implement but not much work since it's just mirroring what SDL3 (and all APIs) already does.

We should have functions to handle this flow:

  • Create effect.
  • Start effect.
  • Update effect.
  • Stop effect.
  • Remove effect.

The reason to do it separately like this is because effects are uploaded into device slots. Creating an effect can have a performance cost depending on the device, so we want to create the effect once and maybe play it many times, update it many times, then remove it when it will be no longer used for a while to free the slot.

Start and Stop can actually be handled in the same call by setting repetition to zero, or just updating the effect with length zero.

We have to also make sure we're handling infinite duration and infinite repetition correctly.

@Nintorch Nintorch force-pushed the steering-wheels-force-feedback branch from 0360006 to 16377ae Compare January 6, 2026 12:16
@Nintorch Nintorch changed the title Add support for SDL haptics Add support for joypad haptics (force feedback) Jan 6, 2026
@Nintorch Nintorch force-pushed the steering-wheels-force-feedback branch from 16377ae to 9621efe Compare January 7, 2026 12:50
@GNSS-Stylist
Copy link
Contributor

Although this PR is draft, I played a bit with this (using only constant_force_feedback). Seems that the force is likely set to 0 for a while every time it is set, at least when the previous value is still in use.

Video (with sound):
https://github.com/user-attachments/assets/55689ffd-5865-4653-82f3-3549d2eb30c0

First tested this with another project, but then made this tiny test-project (as seen in the video) to understand the behavior better:
ffb-test.zip

In https://github.com/Dechode/Godot-FFB-SDL (that I originally used in the "another project" mentioned) there are no these kind of glitches.

Steering wheel used: Logitech Driving Force GT (yes, it's old and noisy, but at least the glitches can be easily heard ;) ), OS: Linux (Mint)

BTW: Compiling this seemed to fail due to some undefined references. Commented these lines out from core/register_core_types.cpp to make it compilable (don't know if worth mentioning as this is draft, though...):

GDREGISTER_CLASS(InputHapticEffectPeriodic);
GDREGISTER_CLASS(InputHapticEffectCondition);
GDREGISTER_CLASS(InputHapticEffectLeftRight);
GDREGISTER_CLASS(InputHapticEffectCustom);

@Dechode
Copy link

Dechode commented Jan 24, 2026

Looking at the code, the behaviour (the wheel rattling) GNSS-Stylist said is because calling constant_force_feedback() starts and plays a new ffb effect, always. When the code is updated to work as Berarma said (and the description of the commit now), we can start, update, stop and remove the effect as we please. Then it should work as intended, as long as the process()/physics_process() only updates the already uploaded effect.

@Spiltdestructor
Copy link

What about GameInput API?

Idk much about APIs
Nor know a lot about the current-used API to help
So sorry if this seems like a stupid question
But maybe that one can help?
Seems to support many Devices out-of-the-box
Technically, supports Linux and can be implemented in Godot
Idk much more
Just wanted to help as I need this too

@Nintorch
Copy link
Contributor Author

Nintorch commented Feb 1, 2026

What about GameInput API?

Idk much about APIs Nor know a lot about the current-used API to help So sorry if this seems like a stupid question But maybe that one can help? Seems to support many Devices out-of-the-box Technically, supports Linux and can be implemented in Godot Idk much more Just wanted to help as I need this too

We thought about using GameInput for SDL, but the license looks incompatible with Godot's MIT license: #107967 (comment)

@Spiltdestructor
Copy link

I see, thx!

@Nintorch Nintorch force-pushed the steering-wheels-force-feedback branch 3 times, most recently from f125636 to 49de134 Compare February 4, 2026 18:07
@Nintorch
Copy link
Contributor Author

Nintorch commented Feb 4, 2026

I have just overcome my biggest problem with this PR: my laziness to write the boilerplate code.

This evening (for me) I wrote a Python script that would read a file in a special format I came up with and it would transform this simple file into a .h and a .cpp files with the generated boilerplate, the resulting files are used here in this PR for core\input\input_haptic_effect.h and core\input\input_haptic_effect.cpp files.
Which means I can continue working on this PR later :D

This script will be useful later when I decide to add InputEvents for joypad motion sensors and touchpads too.

I open-sourced this script in case anyone would find it useful too :D
https://github.com/Nintorch/GodotBoilerplateGenerator

@Nintorch Nintorch force-pushed the steering-wheels-force-feedback branch 2 times, most recently from 416af54 to f33b8bc Compare February 5, 2026 14:50
@Nintorch
Copy link
Contributor Author

Nintorch commented Feb 5, 2026

I have rewritten the code, so when the CI finishes you guys can try it out, please let me know if it works! :D

@Nintorch Nintorch force-pushed the steering-wheels-force-feedback branch from f33b8bc to 5ed63b4 Compare February 5, 2026 14:59
@Nintorch Nintorch force-pushed the steering-wheels-force-feedback branch from 5ed63b4 to a6afa34 Compare February 5, 2026 15:06
@GNSS-Stylist
Copy link
Contributor

Tried to test this. Compiled fine, but don't really know how to use this. Code like this runs:

var haptic:InputHapticEffectConstant = InputHapticEffectConstant.new()
haptic.direction_radians = 0
haptic.duration = $SpinBox_Time.value
haptic.force = newForce
haptic.attack_length = 0
haptic.fade_length = 0

But of course this doesn't do anything. Looked at the commit and expected that the effect needs to be activated by using something like "Input.create_joy_haptic_effect(haptic)", but Godot doesn't recognize the function (it recognizes other functions from the same "block" on input/input.h like set_joy_motion_sensors_enabled). Then tried to add those functions to Input::_bind_methods() (pretty much shooting in the dark because those other function were there):

// My additions:
ClassDB::bind_method(D_METHOD("create_joy_haptic_effect"), &Input::create_joy_haptic_effect);
ClassDB::bind_method(D_METHOD("start_joy_haptic_effect"), &Input::start_joy_haptic_effect);
ClassDB::bind_method(D_METHOD("update_joy_haptic_effect"), &Input::update_joy_haptic_effect);
ClassDB::bind_method(D_METHOD("stop_joy_haptic_effect"), &Input::stop_joy_haptic_effect);
ClassDB::bind_method(D_METHOD("remove_joy_haptic_effect"), &Input::remove_joy_haptic_effect);

But this fails to compile. So I'm a bit confused. How this should be used?

@Nintorch
Copy link
Contributor Author

Nintorch commented Feb 5, 2026

Oh, you're right, I forgot to register the methods...
Let me fix it quickly :D

@Nintorch Nintorch force-pushed the steering-wheels-force-feedback branch 2 times, most recently from b69b2c7 to 5ca0779 Compare February 5, 2026 19:20
@Nintorch Nintorch force-pushed the steering-wheels-force-feedback branch from 5ca0779 to 63a906d Compare February 5, 2026 19:39
@Nintorch
Copy link
Contributor Author

Nintorch commented Feb 5, 2026

Fixed!

@GNSS-Stylist
Copy link
Contributor

That was fast!
Tested InputHapticEffectConstant with Logitech Driving Force GT / Linux (Mint). Works nicely without the glitches mentioned earlier. Updated test project: ffb-test-V2.zip

Also tested quickly attack_length and fade_length-parameters and they seemed to work too.

Needed a bit of trial and error to get working so may need some documentation (if not done already? Don't know how the documentation process goes).

@Nintorch
Copy link
Contributor Author

Nintorch commented Feb 6, 2026

That's good to hear, thank you for testing! :D
Yeah, I plan to add documentation for the features and maybe add better default values to the effect parameters, so the effects can be used more easily.

@Dechode
Copy link

Dechode commented Feb 8, 2026

Works for me too, using linux and g29. I tried it with the ffb-test-v2 project GNSS-Stylist provided and it worked as it should.

@Nintorch
Copy link
Contributor Author

Nintorch commented Feb 8, 2026

Thanks for testing! :)
(May I ask if you guys also can help with the documentation for this system, if you'd like? Since outside of SDL's documentation I personally don't know what most of this does and I think I don't have a steering wheel that supports these features 😅)

@Dechode
Copy link

Dechode commented Feb 9, 2026

I can help with the documentation where i can. But i must say, i have never used other effects other than the constant force effect, so i don't have a lot of experience.

@Nintorch
Copy link
Contributor Author

What about GameInput API?

Idk much about APIs Nor know a lot about the current-used API to help So sorry if this seems like a stupid question But maybe that one can help? Seems to support many Devices out-of-the-box Technically, supports Linux and can be implemented in Godot Idk much more Just wanted to help as I need this too

I have found a way to use GameInput in Godot! #116055
But SDL doesn't have a GameInput backend in the haptics subsystem, which means DirectInput will still have to be used :(

@Spiltdestructor
Copy link

Spiltdestructor commented Feb 13, 2026

I have found a way to use GameInput in Godot! #116055
But SDL doesn't have a GameInput backend in the haptics subsystem, which means DirectInput will still have to be used :(

Well! Progress Is progress! Even if it doesn't end to anything rn, but maybe there Is another way later on
Reading the PR tho, only issue that might be Is that, due to It not supporting input when not in focus, it May break some apps that rely on It (Say an app that records the Screen and Waits for a specific Keybind or Controller Input)

Fallback could be useful still, those are issues Which you seem to be already addressing

Honestly you are carrying the entire PR lol
I'll try Reading some of your code since I know C++, maybe I can understand what's happening and see if I can help with Anything

Edit: Nvm, I expanded the PR, you got It working lol, nice! Tho nice coincidence that everyone here has a G29 XD

@Spiltdestructor
Copy link

OHHH
I JUST SAW IT WAS YOU WHO ADDED THE LED CHANGE FOR GAMEPADS IN GODOT
NICE!

@Nintorch
Copy link
Contributor Author

Nintorch commented Feb 13, 2026

Well! Progress Is progress! Even if it doesn't end to anything rn, but maybe there Is another way later on

Thanks, and yeah, I hope someone would be able to make a GameInput haptics backend for SDL at some point, then we'll be able to fully switch from DirectInput!

Reading the PR tho, only issue that might be Is that, due to It not supporting input when not in focus, it May break some apps that rely on It (Say an app that records the Screen and Waits for a specific Keybind or Controller Input)

By the way, it's been fixed! Now the input when not in focus does work! :D

Honestly you are carrying the entire PR lol
I'll try Reading some of your code since I know C++, maybe I can understand what's happening and see if I can help with Anything

Thanks, any help would be appreciated! :)

OHHH
I JUST SAW IT WAS YOU WHO ADDED THE LED CHANGE FOR GAMEPADS IN GODOT
NICE!

Yes indeed it was me! :D

@Nintorch Nintorch closed this Feb 13, 2026
@Nintorch Nintorch reopened this Feb 13, 2026
@Nintorch
Copy link
Contributor Author

Oops, misclick 😅

@Spiltdestructor
Copy link

Thanks, and yeah, I hope someone would be able to make a GameInput haptics backend for SDL at some point, then we'll be able to fully switch from DirectInput!

Np!
And yeah I hope so too!

By the way, it's been fixed! Now the input when not in focus does work! :D

Hell yeah! Very nice! 👏👏👏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for force feedback in steering wheels

7 participants

Comments