DS5Dongle — Studio

Wireless DualSense-to-PC bridge on a Raspberry Pi Pico 2 W: audio-derived haptics for games that have none, adaptive triggers, gyro aiming, right-stick inversion, and a browser-based configuration portal.


Project maintained by artzox Hosted on GitHub Pages — Theme by mattgraham

DS5Dongle — Studio

Version 1.22.0

▶️ Configure in your browser — the config portal can run as a web page, no download required. Needs Chrome or Edge, with the dongle plugged in.

📥 Shared game profiles · 🎮 Trigger effects — ready-to-import per-game configs and adaptive-trigger effects captured from real games; download one and load it in the portal above.

A firmware modification for the DS5Dongle (a Raspberry Pi Pico 2W-based wireless DualSense dongle) that adds audio-derived haptics for games without native DualSense support, full DS4Windows compatibility, converted-rumble blending, a controller-speaker effect leak for added immersion, and custom captured trigger effects — record a real adaptive-trigger effect from a game that has one and replay it in games that don’t — all configurable from a web-based portal.

⚠️ Hardware requirement — an RP2350 board. Two are supported:

  • Raspberry Pi Pico 2 W — the released .uf2 is built for this board. Flash it and you’re done.
  • Waveshare RP2350B-Plus-W (USB-C, 16 MB flash, RM2 wireless) — a prebuilt ds5-v1.22.0-waveshare.uf2 now ships with each release; flash that and you’re done. It is built against pico-sdk 2.2.0, as this board requires. It has not yet been confirmed on hardware by anyone — if you have this board, a report either way is very welcome. To build it yourself instead, one command:

    Windows:  powershell -ExecutionPolicy Bypass -File tools\build-windows.ps1 -Variant waveshare
    macOS:    ./tools/build-macos.sh --waveshare
    

    These fetch the right toolchain and SDK for you and write ds5-bridge-waveshare.uf2 into its own build directory. This board needs pico-sdk 2.2.0, newer than the 2.1.1 used for the Pico 2 W build — and the difference matters: against 2.1.1 this target still compiles, but the resulting firmware does not work properly, because the RM2 wireless needs 2.2.0. If you build by hand with boards/build_waveshare_rp2350b_plus_w.sh, it now checks your SDK version and refuses rather than handing you a silently broken build.

The original Raspberry Pi Pico W (RP2040) is not supported — it is a different chip (Cortex-M0+ vs Cortex-M33, less memory, no FPU), so a binary built for one cannot run on the other, and this firmware’s floating-point audio DSP (auto-haptics, the effect leak, resampling) is designed around the RP2350’s FPU and larger RAM. Check which board you have before flashing.

Built on awalol/DS5Dongle v0.7.0, which relocates the entire BT/USB/audio path to RAM so native fine haptics and controller audio work without overclocking.


Contents


What this adds over the base firmware

The Wake PC on PS Button feature (USB remote wakeup) originates in the awalol v0.7.0 base and is exposed here as a portal toggle, substantially reworked. It is off by default; enable it only if you want the controller’s PS button to wake the PC from sleep.

With wake enabled the bridge stays on the USB bus whenever the controller is switched off — it has to, or it could never tell that the PC went to sleep — but it does so under a separate USB identity, so nothing appears as a controller that is not there. The details, and what you will see in Windows, are in the callout below.

Wake is a per-profile setting — on for auto-haptics games, off for native ones. Enabling wake changes the controller’s USB descriptor (it advertises USB 2.1 with a BOS descriptor and adds a keyboard interface, which the wake mechanism requires). The altered descriptor no longer looks like a “pure” DualSense, so games with native DualSense support can stop recognising it — a game such as Ratchet & Clank may fall back to Xbox-style rumble instead of native haptics, and the controller’s speaker audio can stop working.

Because wake is one of the settings that requires re-enumeration, a profile that switches it takes the dongle off USB for a second or two. Anything that talks to the dongle right afterwards has to wait for it to come back — the Playnite automation handles this, see automation/AUTOMATION-README.md.

That only matters for games that use the controller natively. For non-native games driven by an auto-haptics profile, nothing is relying on native recognition — the haptics are synthesised from game audio — so wake can safely stay on there.

With wake on, the dongle stays present on USB even when the controller is switched off — it has to, or there would be nothing on the bus for the PC to suspend, and it could never tell the PC had slept. This is what lets the PS button wake the PC even when you switched the controller off before putting the PC to sleep — the usual case, and the one that fails outright if the dongle leaves the bus. In that state it appears under a different USB identity — different vendor and product IDs and its own name — so no controller shows up in DS4Windows or anywhere else; its normal identity returns when the controller reconnects.

What you will see while the controller is off. Windows treats the idle identity as its own device, listed as DS5Dongle (controller off). It is visible in joy.cpl (Game Controllers) and under Settings → Bluetooth & devices → Devices. This is expected and is not a phantom controller: it is the device the PC will be woken from. It also has its own Allow this device to wake the computer setting — usually granted automatically, and the portal’s wake diagnostics will tell you if it was not.

First appearance switches your audio output. Because the idle identity is a new device carrying the same audio interfaces, Windows discovers it as a fresh sound device and, by default, makes it the active output the first time it appears — so PC audio goes silent (or to the wrong endpoint) until you switch back. Set your normal output device again in the volume flyout or Sound settings; Windows remembers the choice, so this is a one-time correction rather than something that recurs.

DS4Windows does not pick it up, by design. The idle identity does not match the controller IDs DS4Windows looks for, so it is not captured — which means DS4Windows stays free to auto-load profiles for any other controller you connect while the DualSense is off. If you would rather not see it at all, it can be hidden with HidHide like any other device, and it then disappears from games as well. Hiding it does not affect waking, which is handled by Windows at the USB level rather than by anything that reads the device.

With wake off the dongle removes itself from USB entirely when the controller disconnects.

Wake PC on PS Button is an ordinary configuration field, so every profile and every on-dongle slot carries its own value. The practical setup is:

  • auto-haptics profiles → wake on
  • native-game profiles → wake off

and the automation switches it per game along with everything else.

One caveat about how the switch is applied. Changing wake forces a USB re-enumeration. Slot activation (slot N in profile-overrides.txt) sends a single command and the firmware applies the whole configuration at once, which handles this cleanly. A field-by-field .html profile applies settings one at a time over the live connection, so a re-enumeration mid-apply can interrupt it — prefer slots for any profile that changes wake.


Quick start

  1. Flash the firmware. (RP2350 only — Pico 2 W and Waveshare RP2350B-Plus-W each have their own prebuilt firmware, or build it yourself; this will not run on the original Pico W.) Hold the BOOTSEL button while plugging in the board (or triple-click BOOTSEL on an already-running unit), then copy ds5-v1.22.0.uf2 (Pico 2 W) or ds5-v1.22.0-waveshare.uf2 (Waveshare) to the RPI-RP2 drive that appears.
    • You do not normally need flash_nuke.uf2 (the one supplied is built for the Pico 2 W). Settings and saved profile slots survive an upgrade — new options are appended to the stored layout, so old values keep their meaning and anything new lands on its default. Only run it if a release note explicitly says to, or if the portal shows settings that are clearly nonsense. It erases every setting and all 32 profile slots, so back up your slots first (Slots tab → Back up all slots).
  2. Open the portal. Download ds5-config-portal.html and open the downloaded file in Chrome or Edge. (WebHID needs a secure context — opening it directly from a website host or file:// that the browser flags will fail with a permissions error. Downloading it and opening the local file works, as does serving it from http://localhost.)
  3. Connect. Click Connect and select the DualSense.
  4. Decide what you need — this determines whether there is anything left to do.

    • Playing games with native DualSense support? Leave Auto Haptics Mode on Off, click Save to Device, and you’re finished. Native games drive the controller’s haptics themselves and the dongle passes them straight through — no Python, no helper, nothing else to install. Native haptics also need two settings outside this project — see Native-haptics games — required setup.
    • Want haptics in games without native support? (Most games.) Set Auto Haptics Mode to Mix or Replace, tune to taste, Save to Device — then continue to step 5.
  5. Mix / Replace only — install and run the audio bridge. Auto-haptics are derived from your PC’s game audio, so a small helper has to capture that audio and feed it to the dongle. Windows only, since it uses WASAPI:

    1. Install Python 3 from python.org — tick “Add python.exe to PATH” in the installer.
    2. Install its one dependency:

      pip install PyAudioWPatch
      
    3. Run it, and leave it running while you play:

      python automation/ds5audio.py
      

    No device flags are needed for most setups — it finds the DualSense on its own (--list, --in-index and --out-index are there for when it picks wrong).

In Mix or Replace the audio bridge is not optional — and it is not part of the Playnite automation. Without ds5audio.py running there is no audio for the firmware to derive from, so auto-haptics will appear to do nothing however they are tuned. That is the single most common reason auto-haptics “doesn’t work”. In Off (native) mode it isn’t needed at all.

(Not sure it’s running? Since 1.18.25 the Haptics tab shows an “Audio bridge: active / not detected” line right under the auto-haptics settings, plus two meters — the audio arriving on the dongle against the haptic the DSP is deriving from it — so you can watch “Haptics out” respond as you tune, and see at a glance whether audio is reaching the dongle at all.)

The firmware ships with safe stock defaults (auto-haptics off, standard buffer), so a fresh flash is safe and native passthrough works immediately. For a tuned auto-haptics + immersion setup, see Suggested setup below.


Native-haptics games — required setup

Games with native DualSense support drive the controller themselves and the dongle passes their haptics straight through. Whether that actually happens is decided by two things outside this project, and both catch people out.

1. Steam: set the controller override to Disabled for that game. Right-click the game in Steam → PropertiesController, and set the override to Disabled. Not Default, and not Enabled — either of those leaves Steam Input in front of the controller, the game stops seeing a real DualSense, and native haptics never arrive at all.

2. DS4Windows may have to be closed. If you use DS4Windows to present an Xbox 360 or DS4 virtual pad, Steam can pick up that virtual controller instead of the DualSense and refuse to treat the game as native. Some titles need DS4Windows closed entirely before launchDoom: The Dark Ages is one. If a game that should have native haptics gives you nothing, close DS4Windows and relaunch it before changing any other setting.

You can stop the second problem happening at all by hiding the DualSense from everything except your native games, so the two worlds never collide. See Mixing native DualSense and virtual controllers in automation/AUTOMATION-README.md.

Also worth knowing for native games: keep Wake PC on PS Button off in those profiles — see the wake note above for why.

Suggested setup

These are good starting values for using auto-haptics with audio passthrough and the effect leak, tuned on a real DualSense over Bluetooth. They are suggestions — adjust to taste — but they give a working, balanced configuration without trial and error. (The firmware does not ship with these as defaults, so there’s no flashing surprise; apply them in the portal and save.)

Auto-Haptics & Speaker Effect Leak

Setting Value
Mode Off (switch to Mix or Replace per game)
Intensity (%) 80 Scales the DERIVED auto-haptics only. Native passthrough and converted rumble have their own levels (see ds5audio --map note)
Smoothness 40
Noise Gate 20
LP Cutoff (Hz) 100
Frequency Split Crossover (Hz) 0 (off)
Low Band Gain 100
High Band Gain 100
Filter Slope 12 dB/oct
Auto-mute Speaker (Replace) Yes
Auto-mute Speaker (Mix) Yes
Lightbar Off in Replace Mode Yes
Converted Rumble Strength (Mix) 50 (range goes to 200; left/heavy renders at 60 Hz, right/light at 160 Hz)
Effect Leak Volume (0=off) 0 (raise to enable)
Effect Leak Sensitivity 50
Effect Leak Decay/Fade-out 80
Effect Leak Attack/Responsiveness 50
Effect Leak Output High-pass (Hz) 1000
Effect Leak Output Low-pass (Hz) 8000
Effect Leak Gate Hold (x5 ms) 20
Effect Leak Max Burst (x5 ms) 0 (off; try 30)
Effect Leak Detection Band (Hz) 2500

General Haptics & Audio

Setting Value
Native Haptics Gain 1.00
Speaker Volume 100
Headset Volume 100
Speaker Gain 2
Sync Speaker & Headset Volume Yes
Lock Volume No
Disable Mic No
Disable Speaker No

Device & Connection

Setting Value
Polling Rate Real-time
Audio Buffer Length 16
Inactive Time (min) 12
Disable Inactive Disconnect No
Disable Pico LED No
Wake PC on PS Button On for auto-haptics profiles, off for native games

Advanced — BT Latency (experimental)

Setting Value
BT Flush Timeout Off (reliable)
BT QoS Latency Off

Notes on the suggested values:


Configuration reference

The portal groups settings into the sections below.

Auto-Haptics & Speaker Effect Leak

Setting Range Default Notes
Mode Off / Mix / Replace Off Off = native/rumble passthrough; Mix = native + derived; Replace = derived only
Intensity (%) 0–200 100 Strength of the audio-derived haptics only (curved response). It does NOT scale the native passthrough or converted rumble — those have their own levels, so the three components are set independently
Smoothness 0–100 40 Higher = smoother/longer decay; lower = snappier
Noise Gate 0–100 20 Suppresses quiet content (dialog/ambience) below a threshold
Native Passthrough in Mix (%) 0–100 100 MIX MODE ONLY: level of the native ch3/4 haptic stream mixed under the derived haptics. See “Choosing the passthrough level” below
Filter Native Passthrough in Mix Filtered / Raw Filtered MIX MODE ONLY: whether the ch3/4 passthrough is low-passed at the LP Cutoff before mixing. See “Filtered or Raw?” below
Auto-Haptics DSP Source ch0/1 / ch2/3 ch0/1 Which channel pair the derived haptics are generated from. The speaker and effect leak always read ch0/1. See “Separating the script feed from game audio” below
LP Cutoff (Hz) 30–200 60 Upper edge of the haptics band — only audio below this drives haptics (content above never reaches the actuators)
Frequency Split Crossover (Hz) 0 / 30–200 0 (off) Divides the haptics band in two at this frequency; 0 = single-band (identical to pre-split firmware). Must sit below LP Cutoff
Low Band Gain 0–100 100 Contribution of content below the crossover (impacts, explosions, engine weight)
High Band Gain 0–100 100 Contribution of the crossover..cutoff range (music bass, voice fundamentals) — lower it to tame music/dialog-driven buzz
Filter Slope 6 / 12 / 24 dB/oct 12 Steeper rejects voice above the cutoff more aggressively

Choosing the passthrough level (Native Passthrough in Mix)

In Mix mode the actuators receive three components: the native ch3/4 stream (scaled by this fader), the derived auto-haptics (scaled by Intensity, shaped by the frequency split and gate), and converted rumble (its own strength knob). The fader exists because ch3/4 mean different things in different setups:

Scenario Mode Set passthrough to Why
Native game, passthrough profile Off (ignored) With auto-haptics Off, ch3/4 always pass at full — the game’s own HD haptics. The fader has no effect in Off
Native game + auto-haptics on top Mix 100 Classic Mix: the game’s real haptics plus derived augmentation
Non-native game (DS4Windows / XB360 / DS4) Mix 0 (or taste) ds5audio’s default duplicate mapping copies the game audio onto ch3/4 — an uncontrollable shadow of the derived haptics. At 0, Intensity/split/gate control the whole output; game rumble stays on Converted Rumble Strength. With the fader at 0 the ds5audio --map choice no longer matters in Mix
Any game Replace (ignored) Replace discards ch3/4 by definition — derived only

Rule of thumb: the fader answers “is there anything REAL on ch3/4?” Native game = yes, keep 100. Non-native = no (it’s a duplicate), set 0.

Filtered or Raw? (Filter Native Passthrough in Mix)

The passthrough has always been low-passed at the LP Cutoff before mixing. That is right for a duplicate feed — in ds5audio’s default mapping (and in VoiceMeeter-style routings) ch3/4 carry a copy of the full-band stereo, and passing that raw sends dialogue and treble straight to the actuators. It is wrong for a game that renders its own haptics, because real haptic content lives well above a typical 80 Hz cutoff: filtering removes exactly the effects the passthrough exists to carry, and no amount of passthrough level brings them back.

Your ch3/4 carries Set to Why
A game’s own native haptics Raw Keeps the game’s effects intact. Pair with --map front so the script does not also write to ch3/4
A duplicate of the game audio (default --map duplicate) Filtered Blocks dialogue/treble leaking to the actuators. Usually better still: set passthrough level to 0
The script feed, because DSP Source is ch2/3 Filtered (or level 0) ch3/4 is raw captured audio here, not haptics — see below

The derived haptics are always filtered by the LP Cutoff, split and gate regardless of this setting; it applies only to the passthrough component.

Separating the script feed from game audio (Auto-Haptics DSP Source)

The speaker and the effect leak always read ch0/1. With the default DSP source that pair must also carry the script feed the derived haptics are generated from — so anything ds5audio sends is also what the speaker plays and what the leak passes. For a game that outputs its own audio effects through the controller speaker (Ratchet & Clank: Rift Apart is the obvious example, with its weapon and gadget sounds sent to the pad), that means the game’s speaker effects arrive mixed with the whole captured PC soundtrack, and there is no way to hear only the former.

Setting DSP Source to ch2/3 breaks that coupling:

ds5audio.py --map rear      # script capture -> ch2/3 only, ch0/1 left clear
Component Reads Result
Derived auto-haptics ch2/3 Generated from the script feed, as before
Controller speaker ch0/1 Only the game’s own native speaker audio
Effect leak ch0/1 Only the game’s native effects pass through

Set Native Passthrough to 0 in this configuration: ch3/4 now carries raw script audio rather than native haptics. On a 2-channel stream the DSP falls back to ch0/1 automatically.

Which setup for which game

Game type ds5audio --map Mode DSP Source Passthrough Filter Rumble
Native haptics (e.g. Returnal, Ragnarök) front Mix ch0/1 100 Raw as taste
Native speaker audio you want isolated (e.g. Ratchet & Clank) rear Mix ch2/3 0 (n/a) as taste
Non-native, DS4Windows / XB360 duplicate Mix ch0/1 0 (n/a at 0) 50–100
Native haptics, no augmentation wanted any Off (full) (n/a) (native)

Every one of these is an ordinary configuration field, so each row can live in its own dongle slot and be selected per game — but note the --map value is a ds5audio argument, not a dongle setting, so profiles that need a different mapping also need the matching launch argument. With the optional Playnite automation this is handled per game in profile-overrides.txt, which passes anything after a further comma to the capture script:

Resident Evil 4 = slot 5, audio, --map front
Ratchet = slot 6, audio, --map rear

A game listed in native-games.txt needs the explicit , audio shown above, or the capture is skipped — the native list excludes exactly those games by default. See automation/AUTOMATION-README.md.

Is the game even sending rumble? (Diagnostics)

The Device tab reports Rumble from host — the peak motor values seen since the last read, plus which rumble flags the game requested. Non-zero while a game vibrates means it sends motor values, which Mix re-creates through Converted Rumble Strength. Zeros while you can still feel vibration with auto-haptics Off mean the game delivers its vibration as haptic audio on ch3/4 instead, which is the passthrough’s job — set the filter to Raw. The values are peak-held, so a short burst between polls still registers.

Continuing the Auto-Haptics & Speaker Effect Leak settings:

Setting Range Default Notes
Auto-mute Speaker (Replace) on/off on Mute controller speaker in Replace mode
Auto-mute Speaker (Mix) on/off off Mute controller speaker in Mix mode
Lightbar Off in Replace Mode on/off off Kills the lightbar glow in Replace (e.g. blue in Xbox360 mode)
Converted Rumble Strength (Mix) 0–200 50 Strength of a game’s rumble re-created on the actuators in Mix mode. Left/heavy renders at 60 Hz, right/light at 160 Hz. Above 100 deliberately overdrives into the limiter for games whose motor values sit low
Effect Leak Volume 0–100 0 (off) Volume of the transient effect leak through the speaker when auto-muted
Effect Leak Sensitivity 0–100 50 How sudden a level jump counts as an effect (higher = more leaks through)
Effect Leak Decay/Fade-out 0–100 40 How gradually effects fade after triggering (~50 ms .. 500 ms)
Effect Leak Attack/Responsiveness 0–100 50 How fast the gate opens (higher = more immediate, less delay)
Effect Leak Output High-pass (Hz) 50–2000 200 Low wall of the leak window (12 dB/oct) — removes deep bass that pops the speaker
Effect Leak Output Low-pass (Hz) 500–12000 8000 High wall of the leak window (12 dB/oct) — cuts treble sizzle/crackle. With the high-pass forms a band-pass “capture window”: only sound inside it leaks. Automatic make-up gain keeps loudness constant as you move the walls
Effect Leak Gate Hold (×5 ms) 0–100 20 (100 ms) Minimum gate-open time per transient + hysteresis; stops the gate chattering (the “choppy/poppy” leak artifact)
Effect Leak Max Burst (×5 ms) 0–100 0 (off) MAXIMUM gate-open time: cuts sustained sounds (dialogue, music) at the cap with a no-retrigger refractory — one short accent instead of duplicating the room audio; shots end within the cap naturally. Try 30 (150 ms) and raise leak volume: the leak becomes punctuation, not a second speaker
Effect Leak Detection Band (Hz) 100–5000 800 Frequency band the transient detector listens to

How auto-haptics works (brief)

The DualSense haptic actuator is a voice coil that cannot render a near-DC signal, so a naive low-pass of the audio produces no motion. This firmware instead uses the bass envelope of the game audio to amplitude-modulate a 90 Hz carrier that sits in the actuator’s responsive band — turning “how much bass” into felt rumble. A noise gate and a steep, configurable low-pass keep dialog and music from triggering the haptics. Under DS4Windows, the firmware keeps the controller in actuator mode (rather than letting rumble reports force it into motor mode) so the derived haptics keep playing.

The effect leak uses transient detection: it tracks fast and slow envelopes of the high-frequency content and opens the speaker only when the level jumps sharply (an onset), so discrete impacts pass while sustained sound stays muted. The output is high-passed to protect the small speaker from low-frequency popping.

Native Haptics Filter

Applies only to native haptics — the signal a game with real DualSense support sends. It does nothing to derived auto-haptics.

Setting Range Default Notes
Smoothing (anti-alias filter) Off / Light / Strong Light Off = raw and gritty (the pre-1.0.4 texture); Light removes the grit while keeping transient snap; Strong is smoothest but can feel muted

Guide: leave this on Light. Reach for Off only if you want the older, harsher texture back, and Strong if a particular game’s native haptics sound buzzy or harsh on your pad.

General Haptics & Audio

Setting Range Default Notes
Native Haptics Gain 1.0–2.0 1.0 Multiplier on native haptic channels
Speaker Volume 0–127 100 Controller speaker volume (also scales haptic strength)
Headset Volume 0–127 100 Headset jack volume
Speaker Gain 0–7 2 Controller speaker gain stage
Sync Speaker & Headset Volume on/off on Tie the two volumes together
Lock Volume on/off off Ignore in-game volume changes
Disable Mic on/off off Disable the controller microphone
Disable Speaker on/off off Disable the controller speaker

Trigger-to-Rumble (R2T)

Routes the rumble signal into the trigger actuators as a buzz.

Setting Range Default Notes
R2T Mode Off / Left / Right / Both Off Which trigger(s) buzz from rumble
Only vibrate when trigger pressed on/off off on = buzz only when the trigger is pulled past ~25%; off = buzz whenever there’s rumble
Strength 0–100 100 Amplitude multiplier on the rumble value
Frequency 1–255 60 Buzz frequency of the trigger effect (higher = finer/tighter buzz)

Guide: enable Both with Only While Pressed = on for a subtle “feel the rumble in your triggers only when you’re using them” effect. With off, the triggers buzz continuously whenever the game sends rumble. If a game drives its own trigger effects, R2T yields to it by default (see Force Override below).

Adaptive Triggers (Stage 1: resistance, Stage 2: push-back kick)

L2-gated constant resistance on R2 — hold the aim trigger and R2 stiffens. On top of that, the push-back kick delivers recoil: while resistance is engaged, each rumble/haptics burst momentarily switches the kicking trigger(s) to a low-frequency vibration thump (or a Bow-effect snap, see Kick style) that knocks the trigger back against your finger, then resistance resumes as the burst fades (hysteresis prevents chatter at the threshold).

R2 and L2 are shown side by side in the portal, as two aligned columns of the same settings — the list below applies to each. The one exception is Kick follows (the kick’s envelope source): it is a single signal both triggers listen to, so it sits in its own full-width row beneath the columns rather than in either one.

Setting (per trigger) Range Default Notes
Mode Off / Gated by trigger / Always / Gated by shoulder Off “Gated by trigger” = the OPPOSITE trigger arms it, analog (L2 arms R2, R2 arms L2). “Gated by shoulder” = the opposite bumper arms it, digital (L1→R2, R1→L2). Any R2/L2 combination is valid
Resistance strength 0–100 70 Resistance intensity (mapped to the effect’s 0–7 range)
Arming threshold 1–255 30 In “Gated by trigger” mode, how far the arming trigger must be pulled (~12% at default). Ignored in shoulder-gated mode (digital on/off)
Resistance start position 0–9 0 Trigger-travel zone where resistance begins (0 = from the start)
Resistance shape Constant / Ramp / Two-stage / Weapon break Constant Constant = flat Strength A. Ramp = linear A→B across the pull (racing: light→heavy gas, heavy→light brake). Two-stage detent = Strength A with a wall of Strength B at the detent zone — a tactile bump marking half-press from full-press (fire/alt-fire games). Weapon break = rigid wall then hardware snap-through at the break point — the semi-auto shot break (Strength B unused)
Strength B 0–100 70 Second strength: the ramp’s end value, or the detent wall
— Strength 0 in shapes     In Ramp/Two-stage, a strength of 0 means genuinely FREE travel (zone excluded): Ramp A=0 = free at rest building to B; Detent A=0 = a pure bump with free travel around it
Detent zone / break point 0–9 5 Two-stage: the wall’s zone. Weapon break: the snap-through point (hw 3–8, forced above start)
Activation dead zone 0–9 0 (off) Below this zone the GAME sees the trigger untouched (no analog, no press bit) — the shot registers only past the zone. Aligns hair-trigger games with the resistance/detent/bow feel; internal effects always see the raw trigger. Not for analog gas/brake inputs
Push-back kick strength 0–100 0 (off) Recoil intensity; scales the thump amplitude with the vibration envelope. 0 = no kick on that trigger (pure resistance)
Kick style Thump / Bow snap Thump Thump = vibration buzz (0x26). Bow snap = mechanical push-back via the Bow effect (0x22): the snap force presses the trigger back against the finger — sharper recoil, experimental (feel varies with hold depth)
Kick thump frequency 10–200 35 Vibration frequency of the kick; lower = heavier knock, higher = buzzier (R2T’s default buzz is 60 for comparison)
Shared setting Range Default Notes
Kick follows Rumble / Audio / Both Both Envelope source for both triggers’ kicks: game rumble (incl. converted DS4Windows rumble), the auto-haptics audio envelope, or the strongest of the two. Unless the trigger is gated, prefer Rumble only — otherwise the generated haptics keep the audio envelope alive and the trigger moves almost constantly

Guide: L2-gated gives a shooter-style “aim to feel the trigger tension” effect without needing native adaptive-trigger support. Resistance wins over R2T vibration while engaged, so you can run R2T Both + AT L2-gated together: the triggers buzz with rumble normally, and R2 stiffens the moment you aim. For recoil, start around kick 60–80, frequency 35, source Both: aim, fire, and each shot thumps R2. Audio as a source means even games with zero rumble kick on gunfire via the auto-haptics envelope. The diagnostics box shows the live push-back envelope (0–255) and a KICK flag — the kick fires at envelope ≥ 32, so if the number stays 0 while the game rumbles, the selected source isn’t producing signal.

Two-stage triggers (new in 1.22.0)

A second action part-way through the trigger’s travel. Pull to the boundary and the game sees the trigger as normal; push past it and a button press is added. Pair it with a weapon-break effect and the wall you feel is the boundary, so the second stage lands at a point your finger can find.

Setting Range Default Notes
Two-stage trigger Off / Add / Add + rescale / Swap / Swap + rescale Off Add keeps the trigger held past the boundary; Swap releases it so only the button remains
Second-stage boundary 0–254 0 (off) Trigger position where the second stage engages
Second-stage button any face, shoulder, stick or trigger button None What the second stage presses

Add suits anything where the trigger must stay held — a racing throttle that also engages a boost. Swap suits a soft-press / hard-press split where the two actions are alternatives rather than layers.

Rescale stretches the travel below the boundary over the full range, so the shortened first stage keeps its full analog resolution instead of being clipped.

The second stage can press the other trigger — R2 can drive L2 and vice versa. That moves the analog axis, not just the digital bit, since games read the triggers as axes. A trigger is never offered as its own second-stage button.

If the button you pick is also what gates that trigger’s resistance, the portal warns: pressing through the detent would open the gate arming the resistance you are pressing against, changing the feel under the finger making the press.

There is hysteresis on the boundary, so holding at exactly the crossing point gives one press rather than a stream of them.

Custom Captured Effects (new in 1.14.0)

Capture a real adaptive-trigger effect from a game that has one, and replay it on any trigger in a game that doesn’t. Effects are stored as the exact 11-byte commands the game sent — they are not decoded into the sliders above (a game’s force curves don’t survive that round-trip), so what you feel is the original effect, not an approximation.

See what an effect looks like. Every effect — whether you build it, load it, or read it back off a trigger — is drawn as a diagram in the portal: trigger travel along the bottom, force up the side, with resistance shown as per-zone steps, a weapon-break wall marking where it gives way, a bow’s ramp and snap point, and vibration as a band over the part of the pull where it plays. Sequenced stages show their hand-off points, and a wall or bow the sequencer cuts short before it fires is faded and marked ✕ — so a truncated effect is obvious at a glance. Since a trigger effect can’t really be conveyed in words, the diagram can be screenshotted and shared. (New in 1.18.25.)

What can and can’t be captured

If the monitor stays empty after you perform the action in-game, that game isn’t sending trigger effects.

1. Capture — Trigger Effect Monitor

  1. In the game, perform the trigger action (pull the weapon trigger, hold the spear…).
  2. Alt-tab to the portal → Triggers tab → Trigger Effect MonitorRefresh captured effects.
  3. The last few distinct effects each trigger received are listed, labelled by type (Resistance / Weapon break / Vibration) with their raw bytes.
  4. Tick the states that make up the action (up to 5) → Assign ticked → custom effect.

Enable and state count are set for you; state count is never set by hand.

2. How this differs from the trigger sliders above

The two systems work on completely different principles, which is why they are mutually exclusive on a trigger:

  Trigger sliders (Adaptive Triggers) Custom captured effects
Where the effect comes from Synthesised from your parameters (strength, shape, start position, detent) Replayed from stored raw effect bytes
How many effects One, continuously recomposed Up to 5 states, one active at a time
What changes the feel Game rumble and audio (kick, R2T, auto-haptics) plus trigger position Trigger position only
Editable Yes — every parameter is a slider No — the bytes are fixed (but you can build new ones)
Multi-stage pulls Not possible (one effect slot; the detent gives one bump, not a second wall) Yes — that is the point

The sliders are a live synthesiser: they rebuild one effect every cycle in response to what the game is doing, which is why they can react to rumble and audio. Custom effects are a player: the states are fixed, and the only thing that decides which one is active is where your finger is on the trigger. Neither can do the other’s job — the sliders can’t stage two walls in one pull, and a captured effect can’t respond to a game explosion.

3. How assigned effects replay — the mode is chosen automatically by type:

How far each stage runs. A stage hands over to the next one shortly before the next stage’s region begins, so the next effect is always armed ahead of your finger rather than snapping into place under it. The consequence is worth knowing when you design a sequence:

So there are three distinct replay behaviours, picked for you:

  1. One state — asserted continuously while the condition holds.
  2. Mechanical sequence (2–5 states, at least one force effect — wall, resistance or bow) — ordered by trigger position and stepped through as you pull. Recorded durations are ignored; position is the only clock. A vibration may be mixed in as one of the stages, and it takes over at its own depth.
  3. Vibration pair or timeline (all states are vibrations) — driven by time, either the A↔B toggle rate or the recorded per-state durations.

A multi-stage effect only sequences under the While held condition. Stages are stepped through by trigger position, which needs the effect armed for the whole pull. Set that trigger to On press or On release and you get a single one-shot of the first stage instead — the later stages simply never play, with no error to tell you why. Assigning from the builder therefore sets the condition to While held (and the zone to 0) for you; assigning from the monitor, a timeline or a file leaves the condition as you have it, so check it if you are building a sequence that way.

4. Timeline recording — for vibration rhythm

The monitor’s history carries no timing. When a vibration’s character is its rhythm (switch-then-hold, pulse patterns), use Timeline recording (Triggers tab, under the effect monitor): press ● Record, perform the action in-game, then Stop & fetch. Every state change is listed with the milliseconds it was held; tick the loopable core (up to 5) and Assign ticked timeline. Replay holds each state for its recorded duration and loops — no rate dialling needed.

Durations are ignored for mechanical states (a wall’s recorded duration is just how long you happened to hold it, not part of the effect), so walls and resistances always sequence positionally even if you assign them from a timeline or load a file that contains durations.

5. Getting a custom VIBRATION to work

This is where the trigger condition matters:

Settings (per trigger)

Setting Range Default Notes
Custom captured effect — enable Off / On Off On = the custom effect owns this trigger’s effect output (see below)
Custom effect trigger condition While held / On press / On release While held While held = armed continuously — required for a multi-stage effect to sequence, and used for a sustained vibration. On press / On release = a one-shot as the trigger crosses the zone, so only the first stage of a sequence would play
Custom effect zone 0–9 0 While held: the re-arm zone — walls reset when the trigger returns below it; 0 = only at full release (finger off, nothing to push against, no click). On press / On release: the engage point — must be ≥ 1
Custom effect A↔B toggle rate 1–100 40 Only for 2-state vibration actions (~2–40 Hz). Ignored for mechanical and timeline effects
Custom effect state count 1–5 Auto-set by Assign / file load; never set manually
Custom effect vs sliders 3 options Custom effect always owns Shares the trigger with the slider stack, gated by the opposite trigger or shoulder button — see Sharing a trigger with the sliders below

Sharing a trigger with the sliders (gate hand-off)

By default a custom effect owns its trigger outright. The Custom effect vs sliders setting lets the two share instead. The gate is the physical gate input — the opposite trigger pressed past that trigger’s threshold, or the shoulder button if its resistance mode is the shoulder-gated one:

Setting Gate engaged Gate not engaged
Custom effect always owns (default) custom effect custom effect
Sliders while gated, custom otherwise synthesized slider effect custom effect
Custom effect while gated, sliders otherwise custom effect synthesized slider effect

With R2’s resistance set to L2-gated, the middle option gives the classic split: hip fire plays your captured multi-stage weapon effect, and the moment L2 passes its threshold R2 switches to the synthesized resistance, kick and all. The third option inverts it — aim down sights to get the captured effect.

Notes:

Recipe: a custom effect and Rumble → Trigger on the same trigger

Only one effect can occupy a trigger at a time, so a custom effect and the rumble-driven vibration cannot both be present at once. They can take turns, though, and the hand-off is what schedules it. The result is a trigger that resists while you aim and buzzes when the shot lands:

Setting Value
Custom effect on L2 your resistance, enabled
Custom effect vs sliders on L2 Sliders while gated, custom effect otherwise
L2 resistance mode Off
L2 threshold the R2 depth at which the swap happens
Rumble → Trigger mode Left trigger only (or Both)

Why it works: the trigger’s order of precedence is game effects → custom effect → slider resistance → rumble → trigger. Rumble sits below the custom effect, so you don’t gate the rumble at all — you gate the custom effect, and rumble becomes what L2 falls back to. With the L2 resistance mode off, there is nothing in between.

In use: hold L2 and you feel your custom resistance. Pull R2 past the L2 threshold and L2 hands over to the rumble vibration, so the shot is felt in the hand that was holding the aim. Release R2 and the resistance comes back, ready for the next shot. Reverse the two triggers for the mirror-image setup.

Two things make or break the feel:

Worth knowing: because only one effect fits, you get one tactile event when the gate engages and another when it releases — not a click, buzz and click, since the resistance ending and the vibration starting are the same transition. How well the window is covered depends on how long the game’s rumble lasts; a very short burst can leave a quiet gap before you release R2.

What works — and what doesn’t — while a custom effect owns the trigger

While the custom effect owns the trigger (always, by default — or only on one side of the gate, if you set the hand-off above), these have no effect on that trigger:

These still work:

The exclusivity is deliberate: the resistance/kick path engages on mode and position even with its strength at 0, so allowing it to run alongside leaked stray kicks (a bow snap on the next press) between custom-effect engagements.

Tip: to keep normal R2T/resistance in one game and captured effects in another, put them in separate profile slots — the automation switches slots per game.

Sharing effects as files

Save ticked to file writes the effect as readable JSON (raw bytes, plus durations for timeline recordings). Load custom effect file → R2 / L2 loads one onto a trigger and sets enable and state count automatically. The portal’s Back up all slots JSON includes each slot’s custom-effect states as well as its settings, so a backup restores a slot complete. (Backups taken before 1.16.0 predate this and contain settings only.)

Building an effect by hand

When there is nothing to capture, the Manual effect builder authors the same kind of effect from scratch. Add stages one at a time and set each one up:

Stage What it is Set
Weapon break A wall the trigger resists, then gives way past Start and end zone, strength
Bow Rising tension that snaps back at the release point Start and end zone, and the two force levels
Vibration A buzz over a range of the pull Zones, frequency, strength

Stages are stacked in pull order and encoded exactly as the firmware’s own writers would emit them, so a hand-built stage is byte-identical to what the sliders — or a game — would have sent for the same values. Built effects behave like captured ones everywhere else: they sit in the same list, play in sequence as you pull, save to the same JSON files, and are stored in slots the same way. Trigger positions are expressed in the same nine zones used throughout the trigger sections, so a wall at zones 2-3 sits where the same numbers put it in a captured effect.

Trigger effects — shared

Ready-to-import adaptive-trigger effects captured from real games live on the Trigger effects page — break-through walls, bows, resistance curves and vibration you can load straight onto L2 or R2 in the portal, then Save to a slot.

Setting Range Default Notes
Force Override on/off off on = force R2T/AT even when a game/app is sending its own trigger effects (off = yield to the game)

Gyro Aiming

Maps controller motion onto the right stick for motion aiming.

Setting Range Default Notes
Gyro Mode Off / L2-held / Always / Touch-enables / Ratchet Off When motion aiming is active (see below)
Sensitivity 1–100 50 Motion-to-stick gain (50 ≈ raw)
Horizontal source Yaw / Roll Yaw Yaw = turn the controller; Roll = tilt it sideways
Invert gyro aim X / Y / both off Per-axis inversion (bit0 = X, bit1 = Y)

Gyro modes:

Guide: L2-held + Yaw is the most natural starting point for shooters — turn the controller to fine-tune aim only when aiming down sights. Raise sensitivity if the motion feels sluggish; use invert if the direction feels backwards.

Right Stick Inversion

Inverts the physical right stick in the input report the PC sees — independent of gyro aiming, and active in any game with no PC-side software.

Setting Range Default Notes
Invert Right Stick X / Y / both off Per-axis inversion of the physical stick (bit0 = X, bit1 = Y). Applies whether or not gyro is on.

Useful for inverted-look setups, or games that only offer inversion on one axis. Because it rewrites the stick values in the report itself it works everywhere, and it composes with gyro aiming — the stick is inverted first, then the gyro delta is added on top with its own invert. (New in 1.18.21.)

Macros (new in 1.19.0)

Bind a controller button press or a touchpad swipe to a keyboard combo. For example you can press R3 + D-pad Up and map to Ctrl+J, if you use the RivaTuner OSD; swipe left across the touchpad and it receives whatever you assigned. No PC-side software is involved — the dongle sends the keystrokes itself over the same HID keyboard interface the wake feature uses.

Everything is edited on the Macros tab.

Column What it does
Checkbox Enables this macro for the current profile. Per-slot — see below.
Name Up to 15 characters, stored on the dongle so your names survive a different PC or a cleared browser.
Record input Press it, then hold the buttons you want or swipe the touchpad, then press Stop.
Record output Press it, then type the key combo on your real keyboard, then press Stop.
+ / Adds or removes a macro row. (The checkbox enables; the minus deletes.)

Up to 32 macros.

Tap or hold — the recording decides

While recording the input, the duration you hold the controller button(s) sets its mode:

You can bind the same controller button twice, once short and once long, exactly the way the PS button gives you one action on a tap and another on a hold.

One consequence worth knowing: if a button press is bound to both a short and a long macro, the short one can only fire when you release, because until then there is no way to tell a short press from the start of a long one. A button press with only a short-time macro fires immediately on press.

Release order is captured

Recording Alt+Tab stores that you released Tab before Alt, and playback reproduces that order. This matters for combos where the modifier must outlive the key.

Some combos cannot be captured in a browser, because Windows or the browser takes them first — anything with the Win key, Alt+Tab itself, and Ctrl+W / Ctrl+T / F11. The recorder tells you when it saw something it could not read; use the Pick button on that row to set the combo by hand — modifier tick-boxes plus a key list, writing exactly what a recording would.

Touchpad swipes

Four directions, distinguished by which half of the pad the swipe starts on and whether you use one or two fingers. A swipe has no release, so it always fires a single pulse — tap-versus-hold does not apply.

Matching is exact: a macro recorded as swipe right, left half only fires on a swipe that starts on the left half.

The two halves are stored differently, and this is the useful part

  Where it lives Scope
The macro definitions Their own area of the dongle’s flash Shared by every profile
Which macros are enabled The profile itself Per-slot

So you define a macro once and then choose, per game, whether it is live. Save a slot and it captures the current set of ticked boxes; the Playnite automation then switches macro sets per game with no extra setup.

Press Save macros to device to store both the definitions and the enable state. The panel warns you when the enable state has changed but not yet been saved.

Enabling the first macro re-enumerates the controller. Macros share the keyboard interface with the wake feature, so switching from “no macros” to “at least one” changes the USB descriptor and the device briefly disconnects and reappears. Switching which macros are on costs nothing, and if Enable Wake is already on the interface is present anyway, so there is no reconnect at all. This is the same constraint as wake: a game with native DualSense support may stop recognising the controller while the keyboard interface is present.

Motion gestures (new in 1.20.0)

A third way to trigger a macro: hold a button and flick your wrists with the controller. Hold L2, flick down then up, release — and the macro fires.

The held button is a gate, not a modifier. It marks when you are “drawing”, so the dongle is not watching your wrist the whole time you play. Recording starts when the gate goes down (button is pressed) and the gesture is matched when you let go.

   
Strokes per gesture 1–4 (up to 8 are stored)
Directions up, down, left, right
Gate any button or combination, held while you flick
Fires on release of the gate

While a gate is held, gyro aiming is suspended — the same wrist movement that makes the gesture would otherwise swing your aim. It resumes the moment you let go.

Recording calibrates to you. Press Record motion, hold the gate, flick the controller, release. The portal measures how far you actually moved and stores a step size with that macro, so a small flick and a broad sweep are both recognised as what you performed. A fixed threshold chosen without your hardware in front of it does not survive contact with a real wrist.

Two behaviours worth knowing, both learned the hard way:

Gestures are macros like any other, so they carry names, live in the same 32 rows, and are enabled per profile.

Backing up and sharing macros (new in 1.19.1)

Definitions and enablement travel separately, because they are stored separately.

Action Carries Asks
Export macros / Import macros (Macros tab) the definitions import confirms — it replaces all of them
Export Profile / Import Profile which macros are enabled import confirms, and names what would fire
Export HTML and the Playnite auto-apply page nothing about macros n/a
Back up all slots / Restore both asks about definitions, both ways

Import macros replaces every macro on the dongle, for all profiles. It loads into the editor rather than writing straight to the device, so nothing is committed until you press Save macros to device.

Importing a profile asks before changing your macro selection, and lists what that selection would switch on. It has to: a profile carries only the enable mask, so it turns on your macros in those rows — which may be nothing like what the profile’s author had there. Decline and your current selection is left alone while every other setting still imports.

The auto-apply page carries no macro information at all. Two reasons, either sufficient. It runs unattended on every game launch, so there is nobody to answer that question — and enabling the first macro re-enumerates the controller, which is the same mid-apply interruption hazard that makes wake unsuitable for a field-by-field .html profile. Per-game macro sets belong on the slot path, where the whole configuration is applied in one command.

Two limits to be aware of

Device & Connection

Setting Range Default Notes
Controller Type DualSense (DS5) / DualSense Edge (DSE) / Auto-detect Auto-detect Which pad identity the dongle presents to the PC. Auto-detect follows the controller actually paired; force one if a game only recognises a specific model. Changing this re-enumerates the device.
Polling Rate 250 / 500 / Real-time Real-time USB report rate
Audio Buffer Length 16–128 64 Lower = snappier haptics/lower latency; higher = more audio stability
Inactive Time (min) 5–60 30 Idle timeout before disconnect
Disable Inactive Disconnect on/off off Never auto-disconnect when idle
Disable Pico LED on/off off Turn off the Pico’s onboard LED
Wake PC on PS Button on/off off Assert USB remote wakeup on PS press to wake the host

Advanced — BT Latency (experimental)

Setting Default Notes
BT Flush Timeout Off Drop stale packets instead of retransmitting. No clear benefit on a strong link; left in for tinkering.
BT QoS Latency Off Request a tighter poll interval. Inconclusive in testing; left in for tinkering.

Modes explained


Notes & known behavior


Building from source

Requires the Pico SDK (2.x) with the Pico 2W board support.

Important: pin TinyUSB to 0.20.0 inside the SDK (cd pico-sdk/lib/tinyusb && git checkout 0.20.0). The SDK’s bundled 0.18.0 fails to build the audio interface (TUD_AUDIO_EP_SIZE). Also run git submodule update --init --recursive so lib/WDL and lib/opus are present.

git clone https://github.com/awalol/DS5Dongle.git
cd DS5Dongle
git checkout v0.7.0
# apply the changes:
git apply /path/to/ds5dongle-v1.0.9.patch
# or copy the files from src/ over the originals

mkdir build && cd build
cmake .. -DPICO_SDK_PATH=/path/to/pico-sdk -DPICO_BOARD=pico2_w
make -j

The resulting ds5-bridge.uf2 is the firmware.

Modified files


Credits & license

This is a derivative of awalol/DS5Dongle, which in turn builds on earlier community work on the DualSense dongle concept.

Auto-haptics origin — thanks to @loteran. The audio-derived auto-haptics in this project were inspired by @loteran’s earlier auto-haptics experiments on the DS5Dongle. The single most important insight came from loteran: relocating state_set to RAM, which is what allows the haptic actuators to fire at stock clock speeds (150 MHz) instead of requiring an overclock. The DSP and supporting code here were rewritten from scratch (carrier modulation, channel detection, the noise gate, the DS4Windows handling, the effect leak, and the config protocol are new), but loteran’s groundwork is what made the whole feature possible. This release would not exist without it — thank you.

Thanks also to the broader DS5Dongle contributors and to awalol for the complete RAM relocation in v0.7.0 that keeps native haptics and controller audio working without overclocking, and for the wake-on-PS-button implementation. (@awalol)

Upstream fixes incorporated. The stuck-rumble fix is based on mik9’s (GitHub handle to confirm) upstream “Fix stuck rumble” commit. The USB suspend/wake hardening (debounced power-off to ride out hub-induced suspends, plus the reconnect grace window) is based on @up2urheadlights upstream pull request #186. Both were adapted to the v0.7.0 base used here. Thank you.

Licensed under the MIT License — see LICENSE. The original awalol copyright notice is preserved as required.


Keeping up to date

Watch releases, not commits. Development happens as a steady stream of commits; those are work in progress and aren’t meant to be flashed. Every tested build is published as a release with its own .uf2, portal and changelog entry. On GitHub, WatchCustomReleases notifies you about those and nothing else.

Updating is drag-and-drop. Hold BOOTSEL while plugging the board in (or triple-click BOOTSEL on a running one) and copy the new .uf2 to the RPI-RP2 drive. Take the portal from the same release — the portal and firmware are released together and are tested as a pair.

Your settings survive. Configuration and all saved profile slots carry across an upgrade: new options are appended to the stored layout, so existing values keep their meaning and anything new starts at its default. There is nothing to back up, nothing to re-enter, and no need for flash_nuke.uf2 unless a release note says so.

How often is up to you. There’s no auto-update and nothing nags you. If your setup works, staying on it is perfectly fine — upgrade when a release mentions a fix or feature you actually want. Release notes say plainly when a change is firmware-only, portal-only or documentation-only, so you can skip the ones that don’t affect you.

Files in this release

Optional: Playnite automation

The automation/ folder adds hands-off Playnite integration: it auto-applies a profile per game and routes game audio to the dongle for audio-driven auto-haptics, switching automatically between native-haptics games and everything else.

Need per-game configs to switch between? The Shared game profiles page collects ready-to-import profiles — import one into a slot and the automation can activate it per game.

Quick start:

  1. Python 3 — only if you play games without native DualSense support. The automation starts the audio bridge for those games, and that needs Python. For any game listed in native-games.txt it applies the native profile and starts no audio capture, so a native-only setup needs no Python — slot switching, wake and per-game profiles all still work.
    • Already installed it for audio routing in step 5 of the Quick start above? It’s the same install — skip this and go to step 2.
    • Otherwise install Python 3 from python.org (tick “Add python.exe to PATH”), then pip install PyAudioWPatch. numpy is optional — it gives cleaner resampling, and is only required by the separate ds5audio_downmix.py helper.
  2. Run automation\ds5-setup.bat — it detects its own folder and generates the Playnite scripts with correct paths, then prints the exact lines to paste into Playnite’s script settings.
  3. Run automation\ds5-policy.bat once (self-elevates). It pre-grants the dongle to the profile pages via browser policy, so automated applies never wait for a Connect click and the grant survives browser restarts. Fully reversible with ds5-policy-remove.bat (Edge/Chrome show “Managed by your organization” while the policy is installed).
  4. To fill in your native-haptics game list, launch each such game once and copy the exact names from ds5-automation.log (each launch logs game: '...') into native-games.txt — no need to type them from memory. Per-game custom profiles are supported too: save a profile into a dongle slot in the portal and add a game = slot 3 rule to profile-overrides.txt (or the file-based form, game = file.html, for exported profiles).
  5. See automation\AUTOMATION-README.md for the full walkthrough.

This is entirely optional — the firmware and config portal work on their own without it. The automation just removes the manual steps if you use Playnite.


Glossary

Terms used throughout this document and in the portal.

Haptics and audio

Actuator — the voice coil in each grip of a DualSense. It replaces the rumble motors of older pads and can reproduce audio-rate signals, which is why haptics on this controller are delivered as sound rather than as motor speeds.

Derived (auto-)haptics — haptic feedback this firmware generates from game audio, for games that have none of their own.

Native haptics — haptic feedback a game renders itself and sends to the controller. Nothing is generated in that case; the firmware only passes it through.

Envelope — how loud a signal is over time, ignoring the waveform itself. The bass envelope of the game audio is what drives the derived haptics.

Carrier — a steady tone the envelope is applied to. The actuators cannot render very low frequencies, so “how much bass” is turned into felt motion by modulating a 90 Hz tone rather than sending the bass itself.

Noise gate — a threshold below which the derived haptics stay silent, so room tone, dialogue and quiet music do not buzz continuously.

LP cutoff (low-pass) — the frequency above which content is discarded when looking for bass. Lower = only deep rumble drives the haptics.

Crossover / split — dividing the audio into a low band and a high band so each grip can be driven by a different part of the spectrum.

Effect leak — with the speaker muted, letting sharp one-off sounds (shots, impacts, clinks) through to it anyway, while sustained dialogue and music stay muted.

Transient / onset — a sudden jump in level, i.e. the start of a sound. What the effect leak detects.

Converted rumble — a game’s rumble instructions re-created on the actuators as vibration, used when the actuators are busy with haptics and cannot be handed to the controller’s own rumble emulation.

Motor values — the two numbers a game sends to request rumble. Left is the heavy/low-frequency motor, right the light/high-frequency one, a convention inherited from pads that really had two different motors.

ch0/1 and ch2/3 — the four audio channels the dongle receives. ch0/1 feed the controller speaker and are what the derived haptics are generated from by default; ch2/3 carry native haptics from a game that sends them.

Native passthrough — how much of ch2/3 is passed to the actuators in Mix mode.

Loopback capture — recording what the PC is playing (rather than a microphone), which is how ds5audio.py gets game audio to send to the dongle.

Trigger effects

The DualSense triggers contain a motor and a clutch that can push back against your finger at chosen points in the pull. The vocabulary below describes what that mechanism is doing.

Zone — a position along the trigger pull, numbered 0 (released) to 9 (fully pressed). Every trigger effect is defined by the zones it acts on.

Resistance — constant opposing force across a range of zones. Feels like the trigger is stiffer, or like pulling through treacle.

Wall — a point in the pull where the trigger suddenly resists much harder, so it feels like it has stopped. It may be a hard stop or something you can push past with more force.

Weapon break — a wall you do push through, the way a real trigger breaks when the sear releases: resistance builds, then gives way suddenly. Defined by where it starts, where it gives, and how hard it holds.

Bow — tension that rises the further you pull, then snaps back when released, like drawing and loosing a bowstring. Defined by a start and end zone and two force levels.

Vibration (trigger) — a buzz felt in the trigger itself over a range of zones, at a chosen frequency and strength. Used for engines, chainsaws, automatic fire.

Push-back kick (Stage 2) — a short vibration burst fired while resistance is engaged, so each shot knocks the trigger back against your finger before resistance resumes. Its “bow-snap” variant uses the bow mechanism for a more mechanical snap instead of a thump.

R2T (Trigger-to-Rumble) — rumble generated from how far you pull a trigger, rather than sent by the game.

Force Override — take control of the triggers even while a game is sending its own effects, instead of yielding to the game.

Custom captured effect — a real effect recorded from a game that has one, stored as the exact bytes the game sent and replayed in a game that has none.

Timeline capture — recording not just an effect but its rhythm, so a sequence of vibrations replays with the original timing.

Device and connection

Enumeration / re-enumeration — the process where the PC discovers a USB device and reads what it is. Settings that change the device’s description (wake, polling rate, audio buffer, mic/speaker) require the dongle to detach and re-attach so the PC re-reads it, which briefly disconnects the controller.

Idle identity — the separate USB identity the dongle presents while the controller is switched off and wake is enabled: DS5Dongle (controller off), with its own vendor and product IDs, so nothing appears to the PC as a controller that is not connected.

Remote wakeup — the USB mechanism a device uses to wake a sleeping PC. It needs the device to still be attached, and needs permission in Windows Device Manager.

Keyboard interface — a second HID device the dongle presents to the PC alongside the gamepad, used to send keystrokes. It is shared by Wake, the PS button shortcut and Macros: it appears when any one of them is enabled and disappears when none is. That appearing and disappearing is what re-enumerates the controller, which is why enabling your first macro briefly disconnects the pad but enabling a second one does not.

Profile slot — a complete configuration stored on the dongle itself, so switching setups is one instant command rather than writing every setting.

Passthrough mode (DS4Windows) — DS4Windows forwarding a game’s rumble to the real controller rather than emulating a different pad.

Macros

Macro — a controller input bound to a keyboard combination. The dongle sends the keystrokes itself over its keyboard interface, so nothing runs on the PC and it works in any application, not only games.

Combo — two or more buttons held together and treated as one trigger, such as R3 + D-pad Up. A combo only fires once every one of its buttons is down, so holding the first one on its own does nothing however long you hold it.

Modifier — Ctrl, Shift, Alt or the Windows key. Macros store modifiers in the same list as ordinary keys rather than in a separate field, which is what allows the order they are pressed and released to be recorded exactly as you performed it.

Release order — which key of a combo lifts first. It matters more than it sounds: Alt+Tab expects Tab to be released while Alt is still held. Recording captures the real order; the Pick button assumes the usual one, last pressed released first.

Tap / long press — whether a macro fires the moment the buttons go down, or only after they have been held past a threshold. You set this by how long you hold the buttons while recording rather than by typing a number.

Swipe — a touchpad gesture used as a macro trigger. Identified by its direction, which half of the pad it starts on, and whether one or two fingers are used, so a swipe recorded on the left half only fires when it starts there.

Macro table — the set of macro definitions, stored once on the dongle and shared by every profile slot. Only which macros are enabled is stored per slot, which is what lets one definition be live in one game and dormant in another.