DS5Dongle — Studio

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


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

DS5Dongle — Studio

Version 1.42.0

Support me on Ko-fi

▶️ 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.

💾 Downloads, source and releases on GitHub — the firmware .uf2 files live under Releases.

📥 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.42.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.42.0.uf2 (Pico 2 W) or ds5-v1.42.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 → Properties → Controller, 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 launch — Doom: 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
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
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

Browser audio bridge — test mode (new in 1.28.0)

This is a test mode, not a replacement for ds5audio.py. It exists so someone can try auto-haptics without installing Python. It cannot be automated, and it does not do everything the script does — see the limits below before deciding which to use.

Auto-haptics are derived from your PC’s audio, so something has to capture what is playing and send it to the dongle. Normally that is ds5audio.py. The Browser audio bridge on the Haptics tab does the same job from the page itself.

Nothing leaves your machine. The captured audio is routed inside the browser straight back out to the dongle’s audio input. There is no server, no upload, and nothing is recorded — the screen share is how Chrome exposes system audio, and the video is discarded the moment capture starts.

It only works from the live page. Open the hosted portal. A copy saved to disk has no persistent origin, so the browser grants audio permission for a single call, forgets it immediately, and never reveals any output devices — the list simply stays empty. Everything else in the portal works fine from a local file; only this needs a real address.

Setting it up:

  1. Open the Haptics tab and scroll down to Browser audio bridge.
  2. Press List output devices, and allow the microphone when asked. That permission is only what makes Chrome willing to name audio devices; the microphone itself is released straight away.
  3. Choose the dongle as the output — it appears as Speakers (DualSense), Speakers (DualSense Edge) or similar, depending on which controller is paired. It is preselected when the name is recognised, but check it: sending to your PC speakers instead is silent at the dongle and looks like a failure.
  4. Load the slot you want first, before starting the bridge — see the warning below.
  5. Press Start bridging, and in Chrome’s dialog pick Entire Screen. System audio is not offered for a single window or tab.
  6. Tick “Share system audio”. It is not on by default, and without it the capture is silent — this is the single most common reason nothing happens.

Watch the line under the buttons: it reads the dongle’s own signal level, so a non-zero peak means audio is genuinely arriving at the device rather than merely leaving the browser.

Playing borderless? Chrome’s “you are sharing your screen” bar stays on top of a borderless window. Click Hide on it once and it gets out of the way.

Set the profile before you start bridging. Switching slots while the bridge is running can produce a howling feedback effect. A slot change can re-enumerate the USB device, which takes the audio endpoint away and brings it back underneath a capture that is still running — and for the moment the routing is in flux the output can find its way back into the capture. Stop the bridge, switch, and start it again.

What it does not do, all of which the script does:

  Script Browser bridge
Starts automatically with a game (Playnite) yes no — the share dialog cannot be automated
Runs unattended, no window yes no — the tab must stay open
--map rear / ch2/3 DSP source yes no — the browser feed is stereo, ch0/1 only
Native passthrough on ch2/3 yes no
Works on a locally saved portal n/a no — hosted page only
Browser and OS Windows Chrome on Windows — see below

Not available on Linux or macOS. Capturing system audio through the browser is a Chrome feature on Windows and ChromeOS only — elsewhere the share dialog offers no “Share system audio” option at all, so there is nothing to route. The same is true of ds5audio.py, which uses WASAPI and is Windows-only for the same reason, so on Linux there is currently no route to derived auto-haptics from either.

Worth separating, though: native haptics do work on Linux. A game or desktop that sends the DualSense’s own 4-channel haptic stream reaches the dongle normally, and the trigger effects, gyro, macros and profile slots are all unaffected — they never involved audio capture. It is only the audio-derived haptics that need a capture tool neither route provides.

If you want per-game profiles switching themselves as you launch, use the script. If you want to feel what auto-haptics does before installing anything, use this.

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 strength reduction (new in 1.42.0)

Weakens the adaptive-trigger motors, in ten steps. It is set on the Triggers tab, in the Trigger Strength section.

Setting Range Default Notes
Trigger strength reduction Off, 1–10 Off Off is full strength; 10 is the weakest

The reduction happens inside the controller: the dongle asks the DualSense to lower its trigger motor power, rather than reshaping the effects it sends. So an effect keeps its shape — where a wall sits, where a bow snaps, how a weapon breaks — and only its force drops.

Because of that, it applies to everything driving the triggers:

It is independent of Force Override: the reduction applies whether the game or the dongle owns the triggers at the time.

Like any new setting, it is only kept in a slot once that slot has been saved on a version that has it. Ported from awalol’s DS5Dongle 0.72 (trigger_reduce).

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 Monitor → Refresh 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 — or onto a mouse — 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)
Vertical sensitivity 0–100 0 (same as above) Vertical gain on its own — usually lower, since a game’s vertical aiming range is much smaller
Horizontal source Yaw / Roll / Player space Yaw Yaw = turn the controller; Roll = tilt it sideways; Player space works out which way is down from the accelerometer and follows how you actually hold it — see below
Invert gyro aim X / Y / both off Per-axis inversion (bit0 = X, bit1 = Y)
Gyro output Right stick / Mouse / Mouse + Flick Stick Right stick What the motion drives — see below
Flick Stick — mouse counts per 360° 500–50000 6500 Calibration, Flick Stick only

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.

Gyro as a mouse (new in 1.24.0)

Gyro output decides what the motion drives.

Right stick is the default and works in any game with no setup. Its ceiling is the stick itself: an absolute input with a dead zone and a limited range, so a fast turn pegs it and a slow one rounds away.

Mouse sends pointer movement instead, which is what a gyro naturally produces — finer at low speed, and it never pegs. The catch is that many games ignore mouse input while a gamepad is present, so this usually needs the pad hidden from that game (HidHide). Selecting it adds a HID interface, so the controller re-enumerates once.

Mouse sensitivity uses the same Sensitivity slider, but expect to run much higher numbers than on the stick — roughly 25–75 is the usable band. The feel is matched across polling rates, so switching between 250 Hz and real-time does not change how far a given movement turns you.

Flick Stick (new in 1.24.0)

Mouse + Flick Stick adds Flick Stick to the right stick, implemented to Jibb Smart’s specification. He invented it, and it is designed to pair with gyro aim rather than replace it:

The stick’s own output is removed from the report while this is on, so the game does not also turn from it. Below 90% deflection nothing happens: the dead zone is deliberately huge because a flick must be intentional.

Flick Stick works even with Gyro Mode off — it is a stick feature — but you almost certainly want the gyro on as well, since that is what handles precision. Always is the simplest choice while you calibrate.

Calibration is required, and it is per game. The dongle converts a stick angle into a number of mouse counts, so it needs to know how far a full turn is in that game:

mouse counts per 360° = (cm per 360°) × (DPI ÷ 2.54)

If you know your cm/360 and DPI, that is the number. Otherwise leave the 6500 default and correct once: flick straight back, see how far you actually turned, then multiply by 180 ÷ (degrees you got). Two tries is usually enough. Flicking back twice is easier to judge than once — you should land exactly where you began.

Turn mouse acceleration off and raw input on in the game. Flick Stick converts an angle into a fixed number of counts and assumes the game turns a fixed amount per count; with acceleration, no single calibration value can be correct. Changing the game’s own sensitivity also means recalibrating.

Player space (new in 1.38.2)

Yaw and Roll each assume a particular grip. Turning the controller drives the aim only while the pad is roughly flat; tilting it sideways only while it is roughly upright. Hold it any other way and part of the movement lands on the wrong axis — a level left-right sweep starts dragging the cursor diagonally, and with the pad on its edge it barely turns the view at all. That is why picking an axis also means committing to a grip.

Player space reads gravity from the accelerometer to work out which way is down, then measures how much your movement turned you about the world’s vertical axis. Flat, tilted up, rolled onto its edge — the aim behaves the same. Vertical aim is corrected the same way, so it stays vertical instead of picking up part of a horizontal turn.

Gravity is low-passed, so a knock does not throw the aim, and it falls back to plain yaw if the reading is not sane gravity (a hard shake, or free fall).

To see the difference: aim with the pad flat, then roll it 45° sideways and make the same movement. On Yaw the cursor goes diagonal; on Player space it does not change. Roll it a full 90° and Yaw stops turning the view horizontally almost entirely, while Player space is unaffected.

Yaw and Roll are unchanged and Yaw remains the default.

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.)

Gyro sensitivity: Natural or Manual (new in 1.32.0)

Two ways to set gyro aiming. Natural (the default on a fresh install) expresses it as a real-world ratio: at 1.0x, rotating the controller 10 degrees turns the view 10 degrees, and the same setting is correct in every game that shares the same mouse counts per 360. Manual is the original slider — a number with no real-world meaning that you tune by feel, per game.

Natural applies to gyro-to-mouse only. Gyro-to-stick tells the game how fast to turn rather than how far, so a rotation ratio has nothing to attach to there, and the setting is ignored.

Setting up Natural — step by step

  1. Set gyro output to Mouse in the Gyro tab.
  2. Calibrate the sensor once. Gyro tab → Gyro calibration → Gyro angle check → Measure, then make one full 360-degree turn during the countdown, ending exactly where you started. Line the controller up with a desk edge or door frame so you can return to it precisely. Repeat two or three times.
    • Reads within 5% of 360? Leave Gyro scale trim at 100.
    • Reads high or low? The panel gives you the trim value to enter — set it, then measure again to confirm it now reads ~360.
  3. Measure the game’s mouse counts per 360. This belongs to the game, not the controller — it is the same number Flick Stick needs. Gyro tab → Gyro calibration → Counts per 360 → press Send 6500 counts (you get a few seconds to switch to the game), read how far the view turned, type that in and press Work it out. Sight a landmark before sending and judge against it. Turn off in-game mouse acceleration and smoothing first, or no single value can be correct. You can also calculate it as (cm per 360) x (DPI / 2.54) if you know your mouse settings.
  4. Pick a multiplier. 10 (=1.0x) is literal 1:1 — the camera tracks your hands exactly, which is precise but only turns as far as your wrists do. Most people run 25-120 (2.5x-12x): lower if the stick does the big turns and the gyro only fine-aims, higher for gyro-led play.
  5. Set a vertical multiplier if wanted. A game’s vertical aiming range is much smaller than its horizontal one, so a lower value than the horizontal is common; 0 follows the horizontal one.

Change games and only step 3 changes. Steps 2 and 4 stay.

Setting up Manual

Set the scale to Manual and adjust Gyro Sensitivity until it feels right. Nothing to calibrate, nothing to look up — but the number means nothing outside that game, so expect to redo it for the next one. Manual is also the right choice when a game’s mouse handling makes counts-per-360 meaningless (forced acceleration or smoothing that cannot be disabled).

Setting Range Default Notes
Gyro sensitivity scale Manual / Natural Natural Natural needs counts per 360; Manual is the original slider
Natural multiplier x10 5-200 10 (= 1.0x) 10 is true 1:1; most play at 25-120
Natural vertical x10 0-200 0 (= same) Separate vertical ratio
Gyro scale trim 50-1000 100 Corrects the sensor’s scale; set from the angle check
Mouse counts per 360 500-50000 6500 Shared with Flick Stick — calibrate once, both use it

Stick to Mouse (new in 1.30.0)

Drives the mouse from a stick, for games that aim better with a mouse than with a stick — or alongside gyro aiming, with the stick doing the large turns and the gyro the fine aim. Both paths feed the same mouse, so they add rather than fight.

Setting Range Default Notes
Drive the mouse from a stick Off / Right / Left Off The chosen stick is centred in what the game receives, so the game does not also turn from it
Speed 0–20000 0 (= 600/s) Mouse counts per second at full tilt. Raise until a full push turns as fast as you want
Vertical speed 0–20000 0 (= same as Speed) Separate vertical rate. Games use a much smaller vertical aiming range, so the gain that feels right for turning is usually too fast for looking up and down — try half of Speed
Deadzone 0–50 % 8 Sticks rest slightly off centre and a mouse never stops, so without a deadzone the view creeps. Applied radially, so diagonals behave
Response curve 10–40 18 Exponent ×10. 10 is linear — twitchy at the centre, slow at the edge. 18 gives fine control near centre and full speed at the edge
Invert stick-to-mouse X / Y / both off Per axis, independent of the physical stick inversion above

Sub-count movement is carried between ticks, so slow stick pressure still moves the pointer instead of being truncated away.

Three things worth knowing. It needs the mouse HID interface, so switching it on or off re-enumerates the device, exactly like setting gyro output to Mouse. It is mutually exclusive with Flick Stick, which also claims the right stick — choosing one clears the other rather than leaving both writing the same report. And as with gyro-to-mouse, a game that reads the pad and the mouse may need the controller hidden with HidHide, or it receives the movement twice.

Touchpad as Mouse (new in 1.37.0)

Uses the touchpad as a trackpad. It is relative: the pointer follows how far your finger moves, so you can lift and reposition exactly as you would on a laptop. Useful from the sofa, where reaching for a mouse is the whole problem.

Settings are on the Device tab, in their own Touchpad as Mouse section.

Setting Range Default Notes
Touchpad as Mouse on/off off Adds the mouse HID interface, so the device re-enumerates once when you turn it on or off
Touchpad Speed 1–250 100 Pointer speed. 100 crosses most of a screen in one swipe
Touchpad Jitter Filter 0–20 1 Ignores finger movement smaller than this many pad counts, so a resting finger does not make the pointer drift
Touchpad Invert None / X / Y / Both None Flips the pointer direction per axis
Touchpad Trackball on/off off Keeps the pointer gliding after you lift off, so one flick can cross a large screen
Trackball Friction 1–100 10 Fraction of speed shed every 100 ms. Higher stops sooner; 10 glides for roughly two seconds

Clicks are not part of this setting, on purpose. The touchpad-click halves are already macro triggers and can output mouse buttons, so left and right click are one macro row each — and you choose which half is which, rather than being given a fixed corner. See the Macros section.

It shares the pointer accumulator with gyro aiming and Stick to Mouse, so the three add rather than fight, and sub-count movement is carried between ticks so slow drags still register.

Two gyro activation schemes use the touchpad themselves and do not combine well with this: only while the touchpad is touched runs the gyro during exactly the drag that is moving the pointer, so both add together and the pointer overshoots, and ratchet pauses the gyro whenever you touch the pad, so you get pointer or gyro but never both. The portal warns when either is selected. Gate the gyro on a trigger or a shoulder instead.

There is no on/off chord. Enablement is per profile and profiles load per game, so a desktop profile can have it on while every game profile leaves it off.

Tilt steering (new in 1.39.4)

Roll the controller like a small steering wheel and it adds to the left stick. It does not replace it: coarse steering stays on the stick, where it is precise, and tilt supplies the fine control on top. The angle comes from gravity, so it never drifts and always returns to centre when you level the pad.

Settings are on the Device tab, in the Tilt Steering section.

Setting Range Default Notes
Tilt Steering on/off off Roll left and right adds to the left stick’s X
Tilt range 1–90° 45 How far you roll the pad for the full effect
Maximum stick added 0–100% 70 Racing games apply their own steering dead zone, so below about 60 nothing happens you can feel. 70 is usable trim; 100 lets tilt steer on its own
Tilt dead zone 0–30° 3 Degrees around level that do nothing, so your natural grip does not steer for you
Invert tilt direction on/off off Flip which way a roll steers
Also tilt forward/back on/off off Leaning adds to the left stick’s Y — for bikes and weight shift, not cars
Maximum stick added, lean 0–100% 70 Separate from steering: how much lean a game wants is not how much steering it wants
Invert lean direction on/off off Flip which way a lean pushes

Calibrate with the live test on the Macros tab. Roll the pad and watch the L stick figure — that is the value the game receives, so you can size the offset against the game’s own dead zone. A setting that looks active in the portal can still be swallowed before it reaches the car.

The Device tab diagnostics show the angles and the offsets being applied, which distinguishes “not running” from “running but too small to matter”.

Tilt-only steering was tried across the SIXAXIS era and consistently judged too imprecise to replace a stick, because the entire steering range lived in a wrist angle you cannot hold steady while being thrown around a track. Adding to the stick avoids that: the stick keeps the coarse work, and tilt only has to supply the last few percent.

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.
Pick (input) Tick the trigger buttons by hand instead of holding them. Same result as recording — useful for anything the controller consumes before it transmits, such as an assigned Edge paddle.
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 touchpad click is a separate thing: it is an ordinary button, so it can be recorded on its own or as part of a chord, and it can hold. A swipe is only stored as a swipe when nothing was held during it — press the pad while swiping and you get the chord, not the gesture.

Left and right clicks are distinguishable. The pad has one physical switch, so the click itself cannot tell you where you pressed — but the finger position is in the same report, so a click is recorded as Touchpad click (left) or (right) depending on which half your finger was on. Three ways to use that:

Recorded as Fires on
Touchpad click (left) left half only
Touchpad click (right) right half only
Touchpad click either half — the generic click

The half is decided when the press starts and held until release, so a finger that moves during the click cannot switch it, and a band of about 8% either side of the centre line is treated as neither half — a click there fires only a generic touchpad-click binding, rather than guessing. Aim for the outer thirds of the pad and the halves are unambiguous.

Clicking a half records as that half alone, so the row reads Touchpad click (left) rather than naming two buttons for one press. The generic binding is still available from Pick when you want either half to fire the same macro, and the three are mutually exclusive there — one press cannot be both “either half” and “this half”.

Macros recorded before this existed are generic clicks and keep firing on both halves. Bind both a generic and a half-specific macro and the specific one wins, the same way a longer chord beats a shorter one. If the switch fires with no finger reported — a knuckle, or the very edge of the pad — the click stays unqualified, so only a generic binding catches it.

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.

DualSense Edge buttons (new in 1.29.0)

The Edge’s two Fn buttons and two paddles are available as macro inputs. They were always in the controller’s report and simply never read.

Read this first: a paddle only reaches the dongle if it is UNASSIGNED. The Edge maps paddles inside the controller, before anything is transmitted. A paddle assigned in Sony’s app — or in the on-board profile you are currently using — is sent as whatever it was mapped to: assign it to Cross and the report says Cross, with the paddle bit never set. It does not arrive as “a paddle” and then also as Cross, so there is nothing here that can intercept or override it. To use a paddle as a macro input, clear its assignment in the Sony app (or select an on-board profile that leaves it empty) — then the paddle bit is set and the dongle can see it. This is also why a paddle can appear to work in one on-board profile and be invisible in another.

Fn + D-pad is the combination to build on. Sony’s own app uses Fn + a face button to switch the controller’s on-board profiles, so chords there fight it — but nothing claims Fn + a D-pad direction, so those cannot collide with normal play or with the app.

The Fn buttons themselves are only partly yours. Fn is reported to the dongle like any other button, but the controller keeps acting on its own combinations at the same time — pressing Fn with a face button still switches on-board profiles whatever you bind here. Build on Fn + D-pad and that does not arise; build on Fn + face and you get your macro and a profile switch.

They behave like any other button otherwise: chords, hold, replace, keyboard, controller and mouse outputs all work, and a standard DualSense simply never sets them.

Macro outputs cover the face buttons, L1/R1, L3/R3, L2/R2, Create, Options, Touchpad click and the four D-pad directions, plus keyboard keys and mouse actions. A D-pad output merges with whatever the player is holding: the injected direction wins on its own axis and the other axis is left alone, so “press Up” presses Up even mid-lean. PS and Mute are not offered — PS collides with the PS-shortcut feature, and Mute toggles a state rather than acting as a button.

Hide input from game works on these too, so an Fn button or paddle bound to a macro can be kept from reaching the game like any other button.

If recording will not capture one, use Pick next to Record input and tick the buttons by hand — it writes exactly the same chord. That is the reliable route for a paddle you have not cleared yet, or for any combination the controller consumes before it is transmitted.

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.

Remapping: hold, replace and other outputs (new in 1.26.0)

A macro row does not have to send a keystroke. Two settings turn it into a remap:

Setting What it does
hold while held The output is asserted while the input is held, instead of firing once. A remap needs this — without it X → Circle taps Circle when you release X.
hide input from game The original input is removed from the report, so the game sees only the replacement. Only available alongside hold while held, and it hides the trigger for every row bound to it, not just its own.
double tap (new in 1.34.0) The row fires on two presses of its trigger within 250 ms. Not available on a hold row or a long press. A controller or mouse output is pressed briefly and released, since a one-shot has to hold the button down long enough for the game to sample it.

Output chooses where it goes:

Record and Pick work the same way for all three. Record captures what you actually do — press the controller button, or click the mouse — and Pick lets you choose by hand. Selecting a controller or mouse output turns hold while held on for you, since that is what a remap almost always means.

A double tap costs nothing unless you use it. A single tap can only be resolved late if a double-tap row exists on the same trigger — until the window closes there is no way to know which was meant — so only that trigger waits. Every other row still fires the instant the button goes down, and a table with no double-tap rows behaves exactly as it did before.

Remapping one trigger onto the other stays analog. L2 → R2 carries the travel across, so a variable throttle stays variable rather than collapsing into an on/off switch. Any other input driving a trigger is a full press, since there is no travel to copy.

Choosing a mouse output makes the dongle present a mouse to the PC, so the controller re-enumerates once — but only when you save, never while you are editing.

One button, a single tap and a double tap, with the button hidden

hide input from game is only offered on a row with hold while held, because suppression is a property of a held row. double tap is the opposite: it cannot be a hold row, since a held key has to go down the moment the button does and cannot wait to see whether a second press arrives. So the two settings are never available on the same row, and it looks as though a double tap cannot hide its trigger.

It can — the hiding just does not have to come from the same row. Suppression applies to the trigger, not to one row, so any held row that names it hides it for every row bound to it. Give the job to a row of its own:

Row Input Output hold while held hide input from game double tap
1 X (leave empty) ✔ ✔ —
2 X your single-tap output — — —
3 X your double-tap output — — ✔

Row 1 has no output at all. Its only purpose is to remove X from the report while it is held, which covers both of the other rows. Rows 2 and 3 are one-shots, so the single tap is held back for the double-tap window and fires only if no second press arrives.

Keeping rows 2 and 3 the same kind matters. A one-shot writes the whole keyboard report while it plays, so any key another row is holding is released for the moment it takes — a held key would blink each time the double tap fires. With both as one-shots there is nothing being held to interrupt.

Sticks as an input (new in 1.26.0)

A whole stick can drive four outputs, one per direction — W A S D being the obvious use. Diagonals press both, and each axis has its own threshold with hysteresis so a stick resting on the edge does not chatter.

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
Lightbar Off on/off off Keeps the controller’s lightbar dark in every haptics mode. Since the haptics mode is per profile, “dark in this game, lit in that one” is a matter of using two profiles. A battery notification still overrides this for the seconds it runs.
Player LEDs Passthrough / Off / Battery gauge Passthrough The five white LEDs under the touchpad. See Player LEDs below
Player LED brightness Bright / Mid / Dim Bright Independent of the lightbar. Kept in every mode, including Passthrough, where the game still chooses which LEDs are lit. Bright leaves brightness to the game
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

Battery notification (new in 1.35.0)

Pulses the controller’s lightbar when the battery drops, as a prompt to go and charge — and again whenever you switch the controller on, so you always know what you are starting with. Three independent stages, each with its own level, colour and number of pulses — 3 amber at 50% and 10 red at 10%, say. A stage fires once and then stops. Nothing keeps flashing until you plug in.

Each stage is a line on the Device tab: a tick to enable it, the level, the number of pulses, a colour picker, and a Test button that runs that stage straight away so a colour can be judged without draining a controller.

Test uses the settings already saved to the dongle. Save before testing, or you will be looking at the previous colour and count.

To see the real thing rather than the Test button, set a stage to 100% and reconnect the controller. Any charge below that qualifies, so it fires through the genuine path — the battery reading, the discharging check and the level comparison — instead of the shortcut Test takes.

Setting Range Default Notes
Battery notification on the controller lightbar on/off off Master switch for all three stages
Stage enabled on/off off Kept separate from the level, so turning a stage off does not lose the level and colour set for it
Notify at battery level 10%–100% — 10% steps only. That is the resolution the DualSense reports its own charge at; a value in between is not something the controller can tell us
Number of pulses 1–20 5 One pulse fades up and back down over about 1.6 s, so 10 pulses runs for roughly 16 s
Colour any — Chosen with a colour picker

How it behaves

This is separate from the Pico LED, which is unchanged and blinks continuously below 10%. The two suit different distances — the Pico LED when the dongle is on the desk in front of you, the lightbar from across the room — and most people will want one or the other rather than both.

Player LEDs (new in 1.41.6)

The five white LEDs under the touchpad normally show a player number, which on a single-player PC tells you nothing. Player LEDs on the Device tab takes them over:

Mode What the LEDs show
Passthrough Whatever the game and controller set — the original behaviour
Off Nothing
Battery gauge A bar filled from the left, one LED per 20%

Reading the battery gauge

The controller reports its charge in 10% steps, so these are the readings that produce each pattern:

Reported charge LEDs
90–100% ■■■■■
70–80% ■■■■□
50–60% ■■■□□
30–40% ■■□□□
20% and below ■□□□□, blinking

A change only shows once the new reading has held for five seconds, so a charge that hovers on a boundary does not make an LED flicker. Charge moves by minutes per step, so the delay is not noticeable.

Player LED brightness (Bright / Mid / Dim) is kept in every mode, independently of the lightbar. In Passthrough the game still decides which LEDs are lit, but a Mid or Dim setting survives a game setting its own brightness. Bright leaves brightness entirely to the game.

This complements the battery notification rather than replacing it: the gauge is something you glance at to see how much is left; the lightbar pulse is a prompt that finds you when it is time to act.

Taking over the LEDs means the game can no longer show a player number. On a DualSense Edge the gauge works alongside the controller’s own on-board profile display. Later standard DualSense hardware revisions are documented as supporting only mirrored patterns, so a left-to-right bar may render symmetrically on those.

The Device tab diagnostics show the whole chain on one line — the mode as the firmware sees it, whether it is running, the battery byte it is reading, and the pattern being sent — so a gauge that looks wrong can be traced to the link that is actually at fault.

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, Watch → Custom → Releases 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.