diff --git a/Dalamud/Interface/Animation/Easing.cs b/Dalamud/Interface/Animation/Easing.cs
new file mode 100644
index 000000000..93b909d98
--- /dev/null
+++ b/Dalamud/Interface/Animation/Easing.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Diagnostics;
+using System.Numerics;
+
+namespace Dalamud.Interface.Animation
+{
+ public abstract class Easing
+ {
+ private readonly Stopwatch animationTimer = new();
+
+ private double valueInternal;
+
+ ///
+ /// Initializes a new instance of the class with the specified duration.
+ ///
+ /// The animation duration.
+ protected Easing(TimeSpan duration)
+ {
+ this.Duration = duration;
+ }
+
+ ///
+ /// Gets or sets the origin point of the animation.
+ ///
+ public Vector2? Point1 { get; set; }
+
+ ///
+ /// Gets or sets the destination point of the animation.
+ ///
+ public Vector2? Point2 { get; set; }
+
+ ///
+ /// Gets the resulting point at the current timestep.
+ ///
+ public Vector2 EasedPoint { get; private set; }
+
+ ///
+ /// Gets the current value of the animation, from 0 to 1.
+ ///
+ public double Value
+ {
+ get => this.valueInternal;
+ set
+ {
+ this.valueInternal = Math.Min(value, 1);
+
+ if (Point1.HasValue && Point2.HasValue)
+ EasedPoint = Lerp(Point1.Value, Point2.Value, (float)this.valueInternal);
+ }
+ }
+
+ ///
+ /// Gets or sets the duration of the animation.
+ ///
+ public TimeSpan Duration { get; set; }
+
+ ///
+ /// Gets the progress of the animation, from 0 to 1.
+ ///
+ protected double Progress => this.animationTimer.ElapsedMilliseconds / this.Duration.TotalMilliseconds;
+
+ ///
+ /// Starts the animation from where it was last stopped, or from the start if it was never started before.
+ ///
+ public void Start()
+ {
+ this.animationTimer.Start();
+ }
+
+ ///
+ /// Stops the animation at the current point.
+ ///
+ public void Stop()
+ {
+ this.animationTimer.Stop();
+ }
+
+ ///
+ /// Restarts the animation.
+ ///
+ public void Restart()
+ {
+ this.animationTimer.Restart();
+ }
+
+ ///
+ /// Updates the animation.
+ ///
+ public abstract void Update();
+
+ private static float Lerp(float firstFloat, float secondFloat, float by)
+ {
+ return (firstFloat * (1 - @by)) + (secondFloat * by);
+ }
+
+ private static Vector2 Lerp(Vector2 firstVector, Vector2 secondVector, float by)
+ {
+ var retX = Lerp(firstVector.X, secondVector.X, by);
+ var retY = Lerp(firstVector.Y, secondVector.Y, by);
+ return new Vector2(retX, retY);
+ }
+ }
+}
diff --git a/Dalamud/Interface/Animation/EasingFunctions/InOutCubic.cs b/Dalamud/Interface/Animation/EasingFunctions/InOutCubic.cs
new file mode 100644
index 000000000..c6e14c3a5
--- /dev/null
+++ b/Dalamud/Interface/Animation/EasingFunctions/InOutCubic.cs
@@ -0,0 +1,28 @@
+using System;
+using Serilog;
+
+namespace Dalamud.Interface.Animation.EasingFunctions
+{
+ ///
+ /// Class providing an "InOutCubic" easing animation.
+ ///
+ public class InOutCubic : Easing
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The duration of the animation.
+ public InOutCubic(TimeSpan duration)
+ : base(duration)
+ {
+ // ignored
+ }
+
+ ///
+ public override void Update()
+ {
+ var p = this.Progress;
+ this.Value = p < 0.5 ? 4 * p * p * p : 1 - (Math.Pow((-2 * p) + 2, 3) / 2);
+ }
+ }
+}