Universal Controller MIDI

Core concepts

Calibration

Calibration teaches Universal Controller MIDI the actual physical range of your sticks and triggers. A 20-second routine that eliminates drift and dead zones at the edges.

Updated

Calibration is the single highest-impact 20 seconds you'll spend in this app. It measures your controller's actual stick range and trigger travel — not the spec range, the physical range after months of wear — so every MIDI value you send hits the full 0–127 cleanly, every time.

Run it once after install, run it again if a stick starts drifting. That's it.

What calibration does

Sticks and triggers report raw values from the controller — typically 0–255 on triggers and a signed 16-bit range on sticks. But no two controllers report the same numbers. A worn DualSense might cap out at 248 instead of 255. A factory-fresh one might rest at value 7 instead of 0.

Calibration captures your controller's true min, max, and rest values, then remaps internally so a fully-pressed trigger is 127 (not 124) and a centred stick is 64 (not 61). The MIDI output stays clean across the full 0–127 range, every time.

{
  "controller": "DualSense#A4B2",
  "leftStick":  { "xMin": -32768, "xMax": 32760, "yMin": -32700, "yMax": 32750, "xRest": -12, "yRest": 8 },
  "rightStick": { "xMin": -32750, "xMax": 32760, "yMin": -32760, "yMax": 32740, "xRest": 4, "yRest": -3 },
  "leftTrigger":  { "min": 2, "max": 252 },
  "rightTrigger": { "min": 0, "max": 248 }
}

The routine

Open Settings → Calibration. The app walks you through five steps, on-screen prompts:

  1. Let go of both sticks and triggers — captures rest values.
  2. Roll the left stick in a full circle, twice — captures min/max on both axes.
  3. Roll the right stick the same way.
  4. Squeeze each trigger fully, twice.
  5. Save. Total time: ~20 seconds.

What "good" looks like

After calibration, the diagnostic panel shows a quality score per axis. If something scores under 80, recalibrate that axis specifically — partial recalibration is a single click. Use this table to decide whether your readings are healthy or something needs attention:

MetricHealthyWornFailing
Stick max range (each axis)≥ 32,70032,000–32,699< 32,000
Stick rest offset±10±25> ±50
Trigger max≥ 250240–249< 240
Trigger rest noise0–12–4> 4
Quality score90–10070–89< 70

When to re-run it

Re-calibrate if you notice drift (the stick reports motion when you're not touching it), edge dropout (max values feel like they're capping below 127), or after any rough handling. Once a month is a good cadence for heavy-use controllers; once a quarter is fine otherwise.

Manual calibration override

The auto-routine works for 95% of cases. If you want to hand-edit the calibration JSON — useful when you're trying to match a controller's calibration to another player's setup, or when you've got data from an external tool — the file lives at ~/UCMIDI/calibration/<controller-id>.json and reloads on next app launch.

{
  "version": 2,
  "controller": "DualSense#A4B2",
  "calibratedAt": "2026-05-12T09:14:22Z",
  "qualityScore": 96,
  "leftStick": {
    "xMin": -32768, "xMax": 32760,
    "yMin": -32700, "yMax": 32750,
    "xRest": -12,   "yRest": 8,
    "innerDeadzone": 0.08
  }
}

Common mistakes

  • Calibrating with a controller that's about to die. Low battery makes the stick ADC noisy. Charge above 50% before running the routine.
  • Half-rolling the stick. If you don't push hard against the bezel for the full circle, the captured max range is short, and you'll cap below 127. Push firmly.
  • Skipping recalibration after a stick replacement. If you've swapped a stick module (common DIY fix for drift), the new sensor has different rest values. Recalibrate immediately.
  • Sharing calibration across controllers. Calibration is keyed by serial. Copying one controller's JSON to another's serial usually feels worse than the default, not better.
  • Treating calibration as a substitute for deadzones. They solve different problems. Calibrate first, then set deadzones. See deadzones.

Calibration vs deadzones

Calibration sets the true range. Deadzones tell the app to ignore tiny movements inside that range. Different concepts, both useful — see deadzones for the other half of the story.

Edit this page on GitHub Updated
ESC

Type to search.