Constants $k_1$, $k_2$ and $k_3$ can be described by more intuitive factors:
$f = \frac{1}{2\pi\sqrt{k_2}}$
$\zeta = \frac{k_1}{2\sqrt{k_2}}$
$r = \frac{2k_3}{k_1}$
By substitution, we get the following definitions for $k_1$, $k_2$, $k_3$:
$k_1 = \frac{\zeta}{\pi f}$
$k_2 = \frac{1}{(2\pi f)^2}$
$k_3 = \frac{r\zeta}{2\pi f}$
Stability precautions
If the resonant frequency $f$ is too high relative to the framerate (dt), the system will become unstable.
Just keeping $f$ low is not enough, as a single lag spike may be enough to break the simulation.
A simple way of looking at it is looking at a first-ordere system:
$$ y[n+1] = A y[n]$$
Here, if $|A|> 1$, the next iteration will be larger than the previous.
For this type of system to be stable, it should converge towards 0.
In the case of a second-order system, by substituting the update of position into the update of
acceleration, we get a system of linear equations, which can be written in this form:
Where A and B are matrices based on the k-constants and T.
Whether A is "greater than 1" (which doesn't really make sense for a matrix) is defined by its eigen values.
However, for this implementation we'll clamp either T or k_2 instead:
$$
T < \sqrt{4k_2+k_1²} - k_1
$$
For $T>$, the computation can be divided, however that will require and extra iteration of computation.
$$
k_2 > \frac{T^2}{4} + \frac{Tk_1}{2}
$$
Clamping $k_2$ to be above this value is not physically correct, but will stabilize the system.
Jitter can also happen (caused by negative eigen values), and the following clamping of $k_2$ can remove this: