DEV Community

studiox-dev-base
studiox-dev-base

Posted on

Twelve UI easing curves, written out so you can read them

Every prototype I start ends up with the same handful of easing functions, rewritten slightly differently each time. Worse, when a project has both a web front end and a Unity client, the two drift apart — the web smoothstep and the C# Smoothstep end up with different arithmetic, and nobody notices until a designer says the button "feels different on mobile."

So I sat down and wrote out the twelve I actually reach for, as closed-form functions with no state and no timers. That constraint is the whole point: if a curve is pure arithmetic on t, the same shape can be written identically in JavaScript and C#, or baked into a lookup table for an engine that would rather not compute it.

You can play with all twelve here. It is one HTML file — open it from disk and it still works.

The shape of the thing

Every curve takes a normalized time t and returns a normalized position. That is it.

const clamp = (t) => Math.max(0, Math.min(1, Number(t) || 0));

const smoothstep = (input) => {
  const t = clamp(input);
  return t * t * (3 - 2 * t);
};
Enter fullscreen mode Exit fullscreen mode

And the same curve in C#, which is what makes the two builds agree:

public static float Smoothstep(float input)
{
    float t = Clamp(input);
    return t * t * (3f - 2f * t);
}
Enter fullscreen mode Exit fullscreen mode

No allocation, no Update() loop, nothing to dispose. You call it with a number and get a number.

The twelve

linear, smoothstep, smootherstep, ease-in-quad, ease-out-quad, ease-in-out-quad, ease-in-cubic, ease-out-cubic, ease-in-out-cubic, ease-out-quint, overshoot-soft, anticipate-soft.

The first ten are the usual polynomial family. smootherstep is worth knowing if you have not met it — it is Ken Perlin's version with a zero second derivative at both ends, which reads as noticeably calmer than smoothstep on long moves:

const smootherstep = (input) => {
  const t = clamp(input);
  return t * t * t * (t * (t * 6 - 15) + 10);
};
Enter fullscreen mode Exit fullscreen mode

The two that break the rules on purpose

This is the part that cost me an afternoon once, so it is worth saying plainly.

overshoot-soft passes 1 before it settles back. anticipate-soft dips below 0 before it moves forward. That is the effect. If you clamp the output to 0-1 — which is a very natural reflex when you are wiring these into a tween — you delete the entire reason those two curves exist.

const overshootSoft = (input) => {
  const t = clamp(input);       // clamp the INPUT
  const x = t - 1;
  const s = 0.8;
  return 1 + (s + 1) * x ** 3 + s * x ** 2;   // the OUTPUT may exceed 1
};
Enter fullscreen mode Exit fullscreen mode

Note where the clamp is. Input gets clamped because time outside 0-1 is meaningless. Output does not, because that is where the character lives.

If your rendering layer genuinely cannot accept a value above 1 — say you are driving an opacity — do not clamp the curve. Pick a different curve.

When you want a table instead of a function

Some engines are happier looking up than computing, and some pipelines want the curve as data they can inspect in a diff. Sampling at 61 points across 0-1 has been enough resolution for every UI move I have needed:

{
  "id": "ease-out-cubic",
  "sampleCount": 61,
  "timeRange": [0, 1],
  "samples": [0, 0.049171, 0.096704, 0.142625, 0.186963]
}
Enter fullscreen mode Exit fullscreen mode

Linear interpolation between samples is visually indistinguishable from the closed form at UI durations. I checked all twelve against their closed forms at 20,001 points: the worst case is ease-out-quint, off by 0.00068 — which is 0.68px on a 1000px move. Most of the others land under 0.3px.

What this is not

It is not an animation library. It does not touch the DOM, does not schedule frames, and has no opinion about how you drive t. You still need your own requestAnimationFrame loop or your engine's tween system.

It is not a spring solver. Springs are stateful and take velocity; these are not that, and if you need real spring physics you want a different tool.

It is not a Unity package. The C# is a plain static class you drop into your project.

Where the code is

The demo page — all twelve curves, the motion, and the source for each — is at studiox-dev-base.github.io/motion-curves, and the repo is here. The page is a single file; view source and every implementation is inline.

I also packaged the whole set — sampled JSON for all twelve, an ES module, the C# class, and a license that allows commercial use in end products — on itch.io for $5. The demo is not a trimmed preview; it runs the real implementations. If reading the page is all you need, that is a completely fine outcome.

Top comments (0)