diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp
index 74744d7f3e..71a2e4031f 100755
--- a/src/input_common/analog_from_button.cpp
+++ b/src/input_common/analog_from_button.cpp
@@ -2,6 +2,10 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
+#include <chrono>
+#include <cmath>
+#include <thread>
+#include "common/math_util.h"
 #include "input_common/analog_from_button.h"
 
 namespace InputCommon {
@@ -11,31 +15,104 @@ public:
     using Button = std::unique_ptr<Input::ButtonDevice>;
 
     Analog(Button up_, Button down_, Button left_, Button right_, Button modifier_,
-           float modifier_scale_)
+           float modifier_scale_, float modifier_angle_)
         : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
-          right(std::move(right_)), modifier(std::move(modifier_)),
-          modifier_scale(modifier_scale_) {}
+          right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_),
+          modifier_angle(modifier_angle_) {
+        update_thread = std::thread(&Analog::UpdateStatus, this);
+    }
+
+    ~Analog() override {
+        update_thread_running = false;
+        if (update_thread.joinable()) {
+            update_thread.join();
+        }
+    }
+
+    void MoveToDirection(bool enable, float to_angle) {
+        if (!enable) {
+            return;
+        }
+        constexpr float TAU = Common::PI * 2.0f;
+        // Use wider angle to ease the transition.
+        constexpr float aperture = TAU * 0.15f;
+        const float top_limit = to_angle + aperture;
+        const float bottom_limit = to_angle - aperture;
+
+        if ((angle > to_angle && angle <= top_limit) ||
+            (angle + TAU > to_angle && angle + TAU <= top_limit)) {
+            angle -= modifier_angle;
+            if (angle < 0) {
+                angle += TAU;
+            }
+        } else if ((angle >= bottom_limit && angle < to_angle) ||
+                    (angle - TAU >= bottom_limit && angle - TAU < to_angle)) {
+            angle += modifier_angle;
+            if (angle >= TAU) {
+                angle -= TAU;
+            }
+        } else {
+            angle = to_angle;
+        }
+    }
+
+    void UpdateStatus() {
+        while (update_thread_running) {
+            const float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
+
+            bool r = right->GetStatus();
+            bool l = left->GetStatus();
+            bool u = up->GetStatus();
+            bool d = down->GetStatus();
+
+            // Eliminate contradictory movements
+            if (r && l) {
+                r = false;
+                l = false;
+            }
+            if (u && d) {
+                u = false;
+                d = false;
+            }
+
+            // Move to the right
+            MoveToDirection(r && !u && !d, 0.0f);
+
+            // Move to the upper right
+            MoveToDirection(r && u && !d, Common::PI * 0.25f);
+
+            // Move up
+            MoveToDirection(u && !l && !r, Common::PI * 0.5f);
+
+            // Move to the upper left
+            MoveToDirection(l && u && !d, Common::PI * 0.75f);
+
+            // Move to the left
+            MoveToDirection(l && !u && !d, Common::PI);
+
+            // Move to the bottom left
+            MoveToDirection(l && !u && d, Common::PI * 1.25f);
+
+            // Move down
+            MoveToDirection(d && !l && !r, Common::PI * 1.5f);
+
+            // Move to the bottom right
+            MoveToDirection(r && !u && d, Common::PI * 1.75f);
+
+            // Move if a key is pressed
+            if (r || l || u || d) {
+                amplitude = coef;
+            } else {
+                amplitude = 0;
+            }
+
+            // Delay the update rate to 100hz
+            std::this_thread::sleep_for(std::chrono::milliseconds(10));
+        }
+    }
 
     std::tuple<float, float> GetStatus() const override {
-        constexpr float SQRT_HALF = 0.707106781f;
-        int x = 0, y = 0;
-
-        if (right->GetStatus()) {
-            ++x;
-        }
-        if (left->GetStatus()) {
-            --x;
-        }
-        if (up->GetStatus()) {
-            ++y;
-        }
-        if (down->GetStatus()) {
-            --y;
-        }
-
-        const float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
-        return std::make_tuple(static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF),
-                               static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF));
+        return std::make_tuple(std::cos(angle) * amplitude, std::sin(angle) * amplitude);
     }
 
     bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
@@ -59,6 +136,11 @@ private:
     Button right;
     Button modifier;
     float modifier_scale;
+    float modifier_angle;
+    float angle{};
+    float amplitude{};
+    std::thread update_thread;
+    bool update_thread_running{true};
 };
 
 std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) {
@@ -69,8 +151,10 @@ std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::Para
     auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine));
     auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine));
     auto modifier_scale = params.Get("modifier_scale", 0.5f);
+    auto modifier_angle = params.Get("modifier_angle", 0.035f);
     return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left),
-                                    std::move(right), std::move(modifier), modifier_scale);
+                                    std::move(right), std::move(modifier), modifier_scale,
+                                    modifier_angle);
 }
 
 } // namespace InputCommon