diff --git a/data/strings/en.ini b/data/strings/en.ini
index 2eb01c1a4..e7a280e29 100644
--- a/data/strings/en.ini
+++ b/data/strings/en.ini
@@ -576,6 +576,8 @@ as = As:
 merged_layers = Duplicate merged layers only
 
 [dynamics]
+stabilizer = Stabilizer
+stabilizer_tooltip = Stabilizer radius to avoid shaky lines
 pressure = Pressure
 pressure_tooltip = Control parameters through the pen pressure sensor
 velocity = Velocity
diff --git a/data/widgets/dynamics.xml b/data/widgets/dynamics.xml
index cd424537a..884770ee5 100644
--- a/data/widgets/dynamics.xml
+++ b/data/widgets/dynamics.xml
@@ -1,7 +1,14 @@
 <!-- Aseprite -->
-<!-- Copyright (C) 2020  Igara Studio S.A. -->
+<!-- Copyright (C) 2020-2021  Igara Studio S.A. -->
 <gui>
   <vbox id="dynamics">
+    <hbox>
+      <check id="stabilizer" text="@.stabilizer"
+             tooltip="@.stabilizer_tooltip" tooltip_dir="bottom" />
+      <slider id="stabilizer_factor" value="16" min="0" max="64" expansive="true" style="mini_slider"
+              tooltip="@.stabilizer_tooltip" tooltip_dir="bottom" />
+    </hbox>
+
     <hbox>
     <buttonset id="values" columns="3">
       <item text="" />
diff --git a/src/app/tools/dynamics.h b/src/app/tools/dynamics.h
index f6360fbb7..459c63b79 100644
--- a/src/app/tools/dynamics.h
+++ b/src/app/tools/dynamics.h
@@ -1,5 +1,5 @@
 // Aseprite
-// Copyright (C) 2020  Igara Studio S.A.
+// Copyright (C) 2020-2021  Igara Studio S.A.
 //
 // This program is distributed under the terms of
 // the End-User License Agreement for Aseprite.
@@ -25,6 +25,7 @@ namespace tools {
   };
 
   struct DynamicsOptions {
+    int stabilizerFactor = 0;
     DynamicSensor size = DynamicSensor::Static;
     DynamicSensor angle = DynamicSensor::Static;
     DynamicSensor gradient = DynamicSensor::Static;
diff --git a/src/app/tools/tool_loop_manager.cpp b/src/app/tools/tool_loop_manager.cpp
index 23a9e2c99..488de1631 100644
--- a/src/app/tools/tool_loop_manager.cpp
+++ b/src/app/tools/tool_loop_manager.cpp
@@ -30,6 +30,7 @@
 #include "gfx/region.h"
 
 #include <climits>
+#include <cmath>
 
 #define TOOL_TRACE(...) // TRACEARGS(__VA_ARGS__)
 
@@ -118,6 +119,8 @@ void ToolLoopManager::pressButton(const Pointer& pointer)
     return;
   }
 
+  m_stabilizerCenter = pointer.point();
+
   Stroke::Pt spritePoint = getSpriteStrokePt(pointer);
   m_toolLoop->getController()->pressButton(m_toolLoop, m_stroke, spritePoint);
 
@@ -163,8 +166,27 @@ bool ToolLoopManager::releaseButton(const Pointer& pointer)
   return res;
 }
 
-void ToolLoopManager::movement(const Pointer& pointer)
+void ToolLoopManager::movement(Pointer pointer)
 {
+  // Filter points with the stabilizer
+  if (m_dynamics.stabilizerFactor > 0) {
+    const double f = m_dynamics.stabilizerFactor;
+    const gfx::Point delta = (pointer.point() - m_stabilizerCenter);
+    const double distance = std::sqrt(delta.x*delta.x + delta.y*delta.y);
+
+    const double angle = std::atan2(delta.y, delta.x);
+    const gfx::PointF newPoint(m_stabilizerCenter.x + distance/f*std::cos(angle),
+                               m_stabilizerCenter.y + distance/f*std::sin(angle));
+
+    m_stabilizerCenter = newPoint;
+
+    pointer = Pointer(gfx::Point(newPoint),
+                      pointer.velocity(),
+                      pointer.button(),
+                      pointer.type(),
+                      pointer.pressure());
+  }
+
   m_lastPointer = pointer;
 
   if (isCanceled())
diff --git a/src/app/tools/tool_loop_manager.h b/src/app/tools/tool_loop_manager.h
index 9ca4c6693..84824a6cc 100644
--- a/src/app/tools/tool_loop_manager.h
+++ b/src/app/tools/tool_loop_manager.h
@@ -1,5 +1,5 @@
 // Aseprite
-// Copyright (C) 2019-2020  Igara Studio S.A.
+// Copyright (C) 2019-2021  Igara Studio S.A.
 // Copyright (C) 2001-2017  David Capello
 //
 // This program is distributed under the terms of
@@ -76,7 +76,7 @@ public:
   bool releaseButton(const Pointer& pointer);
 
   // Should be called each time the user moves the mouse inside the editor.
-  void movement(const Pointer& pointer);
+  void movement(Pointer pointer);
 
   const Pointer& lastPointer() const { return m_lastPointer; }
 
@@ -97,6 +97,7 @@ private:
   gfx::Region m_nextDirtyArea;
   doc::Brush m_brush0;
   DynamicsOptions m_dynamics;
+  gfx::PointF m_stabilizerCenter;
 };
 
 } // namespace tools
diff --git a/src/app/ui/dynamics_popup.cpp b/src/app/ui/dynamics_popup.cpp
index 9da937a8d..60f27b79e 100644
--- a/src/app/ui/dynamics_popup.cpp
+++ b/src/app/ui/dynamics_popup.cpp
@@ -215,6 +215,19 @@ DynamicsPopup::DynamicsPopup(Delegate* delegate)
   , m_ditheringSel(new DitheringSelector(DitheringSelector::SelectMatrix))
   , m_fromTo(tools::ColorFromTo::BgToFg)
 {
+  m_dynamics->stabilizer()->Click.connect(
+    [this](){
+      if (m_dynamics->stabilizer()->isSelected() &&
+          m_dynamics->stabilizerFactor()->getValue() == 0) {
+        // TODO default value when we enable stabilizer when it's zero
+        m_dynamics->stabilizerFactor()->setValue(16);
+      }
+    });
+  m_dynamics->stabilizerFactor()->Change.connect(
+    [this](){
+      m_dynamics->stabilizer()->setSelected(m_dynamics->stabilizerFactor()->getValue() > 0);
+    });
+
   m_dynamics->values()->ItemChange.connect(
     [this](ButtonSet::Item* item){
       onValuesChange(item);
@@ -254,6 +267,14 @@ DynamicsPopup::DynamicsPopup(Delegate* delegate)
 tools::DynamicsOptions DynamicsPopup::getDynamics() const
 {
   tools::DynamicsOptions opts;
+
+  if (m_dynamics->stabilizer()->isSelected()) {
+    opts.stabilizerFactor = m_dynamics->stabilizerFactor()->getValue();
+  }
+  else {
+    opts.stabilizerFactor = 0;
+  }
+
   opts.size =
     (isCheck(SIZE_WITH_PRESSURE) ? tools::DynamicSensor::Pressure:
      isCheck(SIZE_WITH_VELOCITY) ? tools::DynamicSensor::Velocity: