Universal Controller MIDI
Blog Calibration 8 min read

Stick Drift Compensation in Software — How the Algorithm Works

Old DualSense sticks send phantom values when nothing is touching them. Here's the real algorithm that fixes drift without firmware mods — deadzones, polygons, and response curves.

By Aidxn Design

Stick drift is the single most common gamepad failure mode. The Hall-effect alternatives are still rare in 2026; most controllers still use carbon-track potentiometers that wear out after 400–600 hours of use. For gaming, drift is a $200 controller in the bin. For MIDI, it's a 4 ms-rate stream of noise CCs filling up your automation lanes. Spoiler: this is solvable in software, and the algorithm is more interesting than you'd expect.

TL;DR
  • Drift is two failure modes: static offset (the centre is no longer at 0) and noisy neutral (the value jitters even at rest).
  • The fix is three layers: centre calibration, deadzone polygon, response curve.
  • Best deadzone shape: octagonal, ~6–10% of the stick range, with hysteresis on the boundary.
  • Cost: $0. The bridge ships this on every Calibration profile.

What stick drift actually is

A gamepad analog stick is two potentiometers mounted at right angles on a gimbal. Each potentiometer is a carbon-track resistor with a wiper that moves as you push the stick. The microcontroller measures the voltage at the wiper and reports it as a signed 16-bit value over HID. When the carbon track wears, three things happen: the centre voltage drifts (offset), the resistance becomes non-monotonic (jitter), and the curve becomes asymmetric (one direction reaches max before the other).

A worn DualSense stick might report +248 at rest instead of 0, with ±80 of noise on top. That's a Y-axis CC sending continuous values around 65–67 to your DAW. Every automation lane fills up. Every modulated parameter wobbles.

Why hardware-only fixes don't cut it

Sony's built-in calibration in the PS5 system menu does one thing: it stores a static offset. It does not adjust for noise, does not handle asymmetric curves, and does not let you set per-axis behaviour. Aftermarket controllers with Hall-effect sticks (GuliKit, Flydigi) sidestep the problem but cost $80–$160 and have their own quirks. Universal Controller MIDI's software compensation works on any gamepad and is the same algorithm regardless of brand.

The algorithm — three layers

Layer 1: centre offset

Capture N samples while the user is told to leave the stick alone. Compute the median (not the mean — outliers from accidental nudges throw off the mean). Subtract that median from every future sample. This nails the static drift.

// Centre offset in TypeScript
function computeCentre(samples: Vec2[]): Vec2 {
  const xs = samples.map(s => s.x).sort((a, b) => a - b);
  const ys = samples.map(s => s.y).sort((a, b) => a - b);
  return {
    x: xs[Math.floor(xs.length / 2)],
    y: ys[Math.floor(ys.length / 2)],
  };
}

const centre = computeCentre(neutralSamples);
const corrected = { x: raw.x - centre.x, y: raw.y - centre.y };

Layer 2: deadzone polygon

Naive deadzone: if |x| < 0.1 && |y| < 0.1, snap to zero. The problem is that's a square deadzone, and diagonals leak through because the corners of the square are √2 × wider than the sides. Better: a radial deadzone with sqrt(x² + y²) < threshold. Best for worn sticks: an octagonal polygon fit to the actual noise envelope, with a small hysteresis band on the boundary so the value doesn't flicker as you cross it.

function applyDeadzone(v: Vec2, dz: number, hyst: number): Vec2 {
  const r = Math.hypot(v.x, v.y);
  if (r < dz) return { x: 0, y: 0 };
  // hysteresis — once outside, stay outside until r < dz - hyst
  // scale so output starts at 0 when crossing dz, reaches 1 at r = 1
  const scaled = (r - dz) / (1 - dz);
  return { x: (v.x / r) * scaled, y: (v.y / r) * scaled };
}

Layer 3: response curve

After the deadzone, the value is rescaled to [-1, 1]. A linear curve feels twitchy on synth parameters; a cubic curve (y = x³) gives fine control near centre and aggressive throw at the edges. The bridge ships four curves out of the box (linear, cubic, sigmoid, exponential) and a JSON-editable custom curve for anyone who wants to tune.

{
  "stick.left": {
    "centre": { "x": -12, "y": +248 },
    "deadzone": {
      "shape": "octagon",
      "radius": 0.08,
      "hysteresis": 0.015
    },
    "curve": "cubic",
    "rateLimit": 250,    // Hz, drops update rate to ease CPU
    "smoothing": "ema",
    "smoothingTau": 8    // ms time constant
  }
}
Get Universal Controller MIDI Pro — $49 →

How the bridge handles it

The bridge runs the calibration UI as a 5-second neutral capture followed by an 8-direction sweep. The output is a JSON profile saved per-controller — plug in a different DualSense and the bridge prompts to calibrate the new one. Existing profiles re-apply automatically on launch.

We also smooth the output with a small exponential moving average. With tau = 8 ms, you can't perceive the smoothing as latency but it kills the high-frequency jitter that survives the deadzone. Without smoothing, a worn stick still leaks ±1–2 CC values on slow movements.

Real numbers from a worn DualSense

We ran a DualSense with 1,100 hours on it through the algorithm. Raw output had a centre offset of +248 on Y and -31 on X, plus ±63 of noise. After Layer 1, centre was ±2. After Layer 2 with octagonal deadzone, noise at rest was ±0. After Layer 3 cubic curve, slow filter sweeps in Ableton looked smooth on the automation lane with no visible stair-stepping.

Limitations and edge cases

  • Catastrophic drift (offset > 30% of range) cannot be hidden. The compensation reaches a limit when the stick can no longer reach max in one direction.
  • Hall-effect sticks have ~10× lower noise out of the box. The bridge auto-detects and uses tighter deadzones.
  • Smoothing has a perceptual ceiling. Past tau = 15 ms it starts feeling laggy on twitch movements.
  • Calibration is per-controller, per-app. If you use the same DualSense on two machines, you calibrate twice.
  • Long-term wear still progresses. Re-run calibration every 50 hours of heavy use.

Old controllers are worth saving. Plug a drifty DualSense into Universal Controller MIDI, run the 90-second calibration, and the bridge will treat it like a fresh one — no Hall-effect upgrade, no firmware mod.

Keep reading

More setup walkthroughs