Pricing Blog
The Nordcraft Blog

Approximating reality with CSS linear()

Unlock intuitive, physics-inspired animations in CSS. Explore interactive examples on how you may utilise the new linear() timing function for great effect!

Approximating reality with CSS linear()
Jacob Kofoed

Jacob Kofoed

July 27, 2025

Linear() is finally baseline and available in all major browsers. This means we can define CSS timing functions using any combination of straight lines—rather than being limited to smooth cubic-bezier curves.

At first glance, this might seem like a downgrade. A classic linear timing function feels flat and robotic—especially compared to the smooth elegance of cubic-bezier(ease). Can you spot the difference in this demo between linear() vs. cubic-bezier: ease.

This was a trick question. Both animations are actually using linear(). The second one just approximates ease using a finely-sampled set of linear points. It feels just as smooth to the eye. But why bother? If ease already exists—why approximate it?

You shouldn't, as you are strictly worse off with an approximation. But here’s the key: linear() isn’t just for mimicking bezier curves. It can approximate any function that maps a time t between 0 and 1 to an output value.

You can approximate any function with linear()

Example: Suppose you want the animation to follow the curve f(t) = t². Sample that function 10 times from 0 to 1 gives us:

  • linear(0 0%, 0.01 10%, 0.04 20%, 0.09 30%, 0.16 40%, 0.25 50%, 0.36 60%, 0.49 70%, 0.64 80%, 0.81 90%, 1 100%)

Each point maps an input time percentage to a new output. In effect, you’re warping time. You can even go negative or overshoot beyond 1. It’s powerful—but not something you’d want to write by hand.

linear() is a compilation target

Even simple linear() timing functions are difficult to read and even harder to visualise. Like gradients, keyframes, or clip-path, the linear() spec was likely not designed to be something you would ever want to write by hand. It’s meant to be generated.

Using Nordcraft, I have set up an interactive tool where you can input any math function (in JS syntax) where t goes from 0 → 1. Try presets or input your own formula. Use the precision slider to control how many steps we sample the function (e.g., 200 steps means t = 0, 0.005, 0.01, ... 1).

You can copy-paste the CSS timing-function if you want to, but we have something even better coming up!

Beyond math: Simulating reality

Math functions are powerful, and beautiful in their simplicity. Some behaviors however — like a bouncing ball or an elastic band—don’t have a clean formula. Instead, we must simulate them. Think about the 3-body problem: there’s no closed-form solution, but with small time-steps and enough compute, we can simulate it accurately. It’s the same technique that helped land a spacecraft on a comet.

This idea applies to UI transitions too. Real-world motion involves friction, mass, gravity, stiffness—things that are hard to describe with a single function as the output of one step is used in the next. But if we simulate them and sample the result, we can convert that into a linear() timing function.

Physics based timing functions

Unlike bezier curves, physics-based motions feel intuitive because they reflect the world we live in. That’s why Apple built “Liquid Glass”, which gladly spends billions of GPU cycles and your phone's battery-life to mimic refraction and light distortion, and why Google spent years researching real world shadows for Material Design. Our world is chaotic, but also predictable by us humans who have lived here a long time. The greatest UX is predictable and intuitive so must work as it would in the real world.

Simulations are inherently compute-intensive. By precomputing and sampling their output, you can store the approximate result as a linear() timing function—cheap to run, tiny in size, and indistinguishable to the user.

Try it yourself

The leftmost bouncing ball is using the function:

  • 1 - Math.abs(Math.cos(t * 3.5 * Math.PI) * (1 - t))

for a bouncy effect, the right uses the bounce simulation that comes packed from Nordcraft that incorporates real world properties set as:

  • Gravity: 2.5
    Bounciness: 0.75
    Drag: 0.5

You can find the project used for all demos here, and experiment with Nordcraft’s animation and timing-function editor.

Lastly, here is a similar demo for a spring-like simulation:

  • Mass: 5
    Stiffness: 100
    Damping: 20

In both demos the Math based approaches does an honourable job, but the details are off. Nothing in the universe with mass can accelerate instantly, yet if you look closely this is exactly what happens in the spring example. In contrast, the simulation first accelerates before it moves, which leads to a more pleasing animation.

At Nordcraft, we use physics-based timing-functions every day. So much in fact, that you can generate spring and bounce timing functions with just a few clicks in the editor. They’re precomputed, snappy, and feel natural to users—because they are natural. Try our timing-function generator in the Nordcraft editor or right here below to copy-paste timing functions anywhere.

  • Notice how the output length of the bounce and spring functions isn’t fixed—it grows with the complexity of the graph. Nordcraft’s linear() functions are generated with ultra-high precision using tiny time steps. After generation, the graph is optimized for quality vs. size using a combination of basic distance-based simplification and the Ramer–Douglas–Peucker algorithm.