A measured mathematical approximation

Cheaper. Easier. Less Math.

The 'square-wheel' or rolling square animation is a result of a catenary function. I see a catenary problem whose exact solution asks for far more math than the final animation needs. This is how I reduced that problem to one line while keeping its worst error below a quarter of a pixel.

Jacob Ammons Applied Mathematics / Performance Engineering Measured in Chromium

The result

Most people have seen a catenary curve before, even if they have never heard its name. If you hang a chain or cable loosely between two points, gravity pulls it into a smooth hanging arc. That arc is a catenary, not a semi circle. If we flip several of those arcs upside down and place them next to one another, we receive the strange road that allows a square wheel to roll while keeping its center level. This is already an interesting mathematical solution, but processing that complete solution for a small decoration on a website would be somewhat absurd.

Instead of calculating the road, I focused on the part the user can actually see: how far the center of the square must move during each quarter-turn. I replaced the exact trigonometric motion with a parabola fitted to the three positions that visually define it—side down, corner down, side down. Across the complete motion this shortcut reproduces the exact center-height curve with 99.30 percent mean normalized accuracy, and at the 84-pixel size used in this report the largest disagreement is only 0.23 CSS pixels. The interesting part is not that the browser is incapable of doing the full math. It is that understanding the math allowed me to prove that almost none of it needed to be done.

Measured outcome

99.30% Mean shape accuracy over the complete 90° interval
0.23px Worst geometric error for an 84px square
2.7× Faster than the direct sin + cos calculation
3 / 3 Critical poses reproduced exactly

Benchmark context: five Chromium runs of 10,000,000 evaluations on Windows produced a median 2.7× ratio against the direct formula. Against a reduced one-cosine identity, the approximation remained approximately 2× faster. These numbers measure this calculation in isolation, not total page CPU usage.

  1. 01 From catenary to shortcut
  2. 02 Why approximation is valid
  3. 03 Math translated into code
  4. 04 Geometry of the square
  5. 05 The complete derivation
  6. 06 Conclusion

Section 01

From a hanging chain to one line of code

To understand why the shortcut is impressive, we should first look at what the complete problem asks us to do.

We begin with one side of the square, convert every possible contact point into a radius and angle, derive how quickly that contact point moves, integrate the horizontal movement, remove our temporary variable, and finally receive the inverted catenary. None of these steps are useless; they are what prove the road is correct. However, they solve a larger problem than the one presented on my home page.

Describe the side (a, t)
Convert to polar r = √(a²+t²)
θ = atan(t/a)
Solve the motion y = −r
dx/dt = r(dθ/dt)
Integrate & eliminate x = a·asinh(t/a)
y = −a·cosh(x/a)

The algebra above is easier to follow once you can see the geometry it is describing. On one side we mark a contact point on the vertical edge, express that point in polar coordinates from the square's center, and integrate the motion until the road profile appears as an inverted catenary.

Concept — Polar setup and the catenary road

One side in polar coordinates

Square side with polar radius and angle A square centered at the origin with its right side at x equals a. A contact point on that side is joined to the center by radius r, which makes angle theta with the positive x-axis. O (a, t) r θ side x = a +x +y

Pick any contact point on the side at x = a. From the center, its distance is r = √(a² + t²) and its direction is θ = atan(t/a).

The inverted catenary

Inverted catenary curve A smooth U-shaped curve dipping below a horizontal reference line, given by y equals negative a cosh of x over a. y = −a cosh(x/a) reference level x

After integrating the horizontal motion and eliminating t, the road profile is a catenary turned upside down. This is the shape the full derivation produces; the home page never draws it.

After all of this, we have a mathematically perfect road for the respective square wheel. The issue is that my website never draws this road. The user only sees a square rotating over a flat section of the page and the center point is moving along this generated curve. Therefore, instead of solving for every point on an invisible road, I asked a smaller question: what are the minimum, maximum, and repeating limits of the movement I can see?

The mathematical estimate

p(φ) = 1 − ((φ − 45) / 45)²

The implemented line

let anglemap =
  1 - Math.pow((cleanang - 45) / 45, 2);

This one line produces a value of zero when a side is flat, one when the diagonal is vertical, and zero again when the next side becomes flat. I then multiply it by the known difference between half a side and half a diagonal. The equation does not blindly imitate the catenary; it extracts the visible consequence of that geometry and represents it with the smallest useful function.

What the processor avoids

Functions such as sin, cos, and cosh are known as transcendental functions. A CPU cannot generally resolve them with one ordinary addition or multiplication instruction. JavaScript engines may use range reduction, polynomial approximations, lookup data, native math libraries, or hardware-assisted routines depending on the browser and processor. Ironically, even the computer commonly approximates these “exact” functions internally.

Since JavaScript is compiled at runtime, there is no honest universal number of CPU cycles that can be assigned to either expression. What I can measure is elapsed execution time. Each timed result is the total wall-clock time for one formula to run through every evaluation in the loop—not the time for a single call. In Chromium 144, one timed pass of 10 million evaluations of my expression completed in roughly 144–169 milliseconds after the engine warmed up. The direct sine-plus-cosine version required roughly 396–421 milliseconds for the same 10 million calls. Even if we use a trigonometric identity to reduce the exact equation to only one cosine, my estimate remains approximately twice as fast.

Run the same benchmark on this computer

Each formula receives one untimed warm-up pass. The timed runs use performance.now() around the full loop, accumulate every result, and rotate formula order between runs. The milliseconds shown are the total time for all evaluations in that run, not per call.

Ready. A 10-million × 5 test may briefly pause the page.

For a useful comparison, keep this tab visible and avoid starting other heavy work while it runs. Results vary with browser version, processor, power mode, temperature, extensions, and background activity. This measures only the isolated formulas—not animation frame rate or whole-page CPU use.

What did I actually save?

The offset calculation uses about 50–63 percent less execution time in isolation, depending on which exact formula we compare it against. At 60 updates per second both versions are inexpensive on a modern desktop, so I am not going to pretend one trigonometric call would melt your computer. What matters is that the cheaper calculation produces nearly identical output, stays constant regardless of screen size, and avoids creating an entire road that never appears on the page.

Section 02

Is approximation bad mathematics?

Why I would knowingly use an inaccurate equation when the entire point of mathematics is to receive an accurate answer. Just to prove a point that not everything has to be done by the book.

Approximations are used everywhere in mathematics. We shorten π, use numerical integration when an antiderivative is impractical, and replace complicated functions with Taylor polynomials that are easier to calculate over a controlled range. The distinction between a poor guess and a useful approximation is that a useful approximation has a defined domain, preserves known properties, and carries an error we can measure.

In my case the domain is only one quarter-turn, from 0 to 90 degrees. I know the normalized lift must pass through the points (0, 0), (45, 1), and (90, 0). There is exactly one quadratic function that passes through all three of those points:

p(φ) = φ(90 − φ) / 2025 = 1 − ((φ − 45) / 45)² This is quadratic interpolation through the three required poses, not a curve chosen by eye.

This is the mathematical reason the estimate works so well. It is exact whenever the square rests on a side, exact when the diagonal reaches its maximum height, symmetrical on either side of that maximum, and continuous throughout the movement. Instead of approximating everything and hoping for the best, I selected the cheapest function that guarantees the most important parts cannot be wrong.

Measuring what remains

Of course, matching three points does not prove everything between those points is accurate. To test the remaining curve, I sampled 1,000,001 evenly spaced angles from 0 through 90 degrees. At each angle I normalized both functions against the maximum lift Δh, measured the absolute difference, and averaged those differences. Mean shape accuracy is defined here as one minus that mean normalized error.

  • 0.7008%Mean absolute error relative to the full lift
  • 0.8372%Root-mean-square normalized error
  • 1.3121%Largest error, occurring near 13.26° and 76.74°

Observing the results, the average error is only 0.7008 percent of the complete lift. For an 84-pixel square that lift is 17.40 pixels, and even the largest error produces a difference of only 0.228 pixels. Put differently, the worst disagreement is 0.272 percent of the square's side length. The equation is not symbolically equal to the exact function, but at the scale it is being used the disagreement is smaller than a rendered pixel.

Figure 1 — Exact motion against the inexpensive estimate
Quarter-turn phase45°
Vertical correction17.40 px
Difference from exact0.00 px
Comparison of exact and approximated vertical correction The exact trigonometric curve and the parabolic site approximation share the same endpoints and midpoint, with a small difference between those points. 45° 90°

Why the estimate holds together

If you move the slider in Figure 1, it becomes incredibly difficult to tell which equation is active without reading the button. At 0 degrees a side is flat and the correction is zero. At 45 degrees the diagonal is vertical and the correction is exactly Δh. At 90 degrees the next side is flat and the correction returns to zero. The slight error between these positions never accumulates because every quarter-turn resets into another exact position.

Exact at the anchors

Zero error at 0°, 45°, and 90° ensures every side and corner arrives where geometry says it should.

Subpixel between them

The largest deviation occurs between the anchors, but remains below one quarter of a pixel at 84px.

Section 03

Translating the math into code

Now that we know why the parabola is mathematically defensible, we can look at how it becomes the square on the page. The live animation is split across two files: JavaScript reads scroll position and computes the vertical correction, then CSS applies the movement. Three pieces work together:

  1. JavaScript in walkingsquare.js sets --rotations and --yoffset from scroll position.
  2. JavaScript in the same file defines calcHeightOffset(...), which returns the parabolic lift in pixels.
  3. CSS in walkingsquare.css reads those custom properties and composes translation plus rotation.

The name calcHeightOffset is easy to misread as CSS calc(), but it is an ordinary JavaScript function defined in walkingsquare.js. The scroll loop calls it; CSS never executes it directly.

1. JavaScript — scroll handler (walkingsquare.js)

// Scroll position becomes the animation parameter.
inc = window.scrollY;
square.style.setProperty("--rotations", inc);
square.style.setProperty(
    "--yoffset",
    -calcHeightOffset(squareHeight, inc) + "px"
);

The call above uses the JavaScript function shown in step 2. The minus sign converts the returned lift into an upward CSS translation.

2. JavaScript — calcHeightOffset(...) (walkingsquare.js)

function calcHeightOffset(height, angle) {
    let deltaH = ((Math.sqrt(2) * height) / 2) - (height / 2);
    let phase = angle % 90;
    let curve = 1 - Math.pow((phase - 45) / 45, 2);
    return curve * deltaH;
}

This is the parabolic estimate from Section 01. It runs in JavaScript once per frame and returns a pixel offset height value. That value is written into --yoffset by the scroll handler in step 1.

3. CSS — transform (walkingsquare.css)

transform:
    translate(
        calc((var(--rotations) / 90) * var(--width)),
        var(--yoffset)
    )
    rotate(calc(var(--rotations) * 1deg));

This separation is useful because JavaScript only has to decide where the square belongs. CSS is then allowed to do what it is good at: compositing the translation and rotation. Instead of changing margins, positions, and dimensions on every frame, I update two custom properties and let the browser's rendering pipeline handle the visible movement.

Encoding the quarter-turn

Inside calcHeightOffset, the vertical correction first uses the remainder operator to reduce any rotation into a phase between 0 and 90 degrees. I then center that phase around 45, divide by 45 to normalize it between negative and positive one, square it, and invert it. The result is our downward-opening parabola. Multiplying it by Δh scales the value from a generic range of zero-to-one into the real number of pixels required by the square.

No road geometry needs to be constructed, sampled, or painted. Each frame resolves one phase and one offset regardless of screen size. In computer science terms this is constant work, or O(1), rather than work that increases with however many points we would need to draw a convincing curve.

Responsive geometry

The square is not assigned one permanent size. Its side length is calculated as a fraction of the viewport width, with a minimum of 75 pixels. Since Δh is derived from the same side length, the correction scales proportionally whenever the page is resized. Without this relationship the animation could be accurate on my monitor and completely wrong on a phone. Keeping the dimensions relative allows the same small equation to survive every screen size.

Section 04

Geometry of the square

A circle is easy to roll because every point on its edge is the same distance from its center. A square has no such convenience.

Let's start with the square sitting flat on one of its sides. If the side length is s, its center is s/2 above the road. As we rotate the square, the road-to-center distance grows until the square balances on a corner. At this exact point the diagonal is vertical, and the center is now half a diagonal above the road, or s√2/2. After the corner passes, the distance falls until the next side lies flat and the entire process repeats.

We can calculate the maximum amount the center must rise by subtracting its original height from half of the diagonal:

Δh = (s√2 / 2) − (s / 2) = s(√2 − 1) / 2 The maximum lift is approximately 20.71 percent of the square's side length.

Now we know the beginning, peak, and ending height without using trigonometry at all. If we wanted every point between them to be exact, we would project the rotated half-width and half-height onto the vertical axis using cos φ and sin φ. This gives us the exact center-height function seen earlier. My animation does not need every intermediate value to be perfect, but it absolutely needs these limits to be correct. The parabola was built around those limits instead of replacing them.

Section 05

The complete catenary derivation

Until now I have discussed the movement I took from the catenary problem. We can now look deeper into how the actual road is produced.

Instead of trying to describe all four sides of a rotating square at once, we can begin with one vertical line segment. Let that side sit at x = a, and let t move from the bottom of the side to the top. Each possible contact point can then be written as (a, t). This rectangular form is easy to picture, but rolling is easier to describe using the point's distance and angle from the center, so we convert it into polar form:

r(t) = √(a² + t²)     θ(t) = arctan(t / a)

Our radius r follows directly from the Pythagorean theorem, while our angle θ comes from the inverse tangent. We can now apply two important properties of rolling. First, the point touching the road must remain directly beneath the center, making the road height the negative radius. Second, the point touching the road is momentarily stationary, so the horizontal distance travelled must equal the small arc swept by that point. Together these properties give us:

y(t) = −r(t)     dx/dt = r(t) · dθ/dt

Substituting our radius and angle gives y(t) = −√(a²+t²) and dx/dt = a/√(a²+t²). The horizontal portion now requires integration, which returns x(t) = a·asinh(t/a). At this point we have a parametric equation, but we can remove t and place everything back in terms of x and y. After moving through this equation chain we finally receive:

y = −a cosh(x / a) An inverted catenary: the hanging-chain curve turned upside down and repeated beneath each side.

The hyperbolic cosine, cosh, creates our catenary. This is the same family of curve formed by a hanging chain, except the negative sign flips it upside down. By repeating one correctly sized section for every side of the square, the road falls away at exactly the same rate the square's corner would normally push its center upward. The two movements cancel, leaving the center on a level line.

This derivation, explored visually in Morphocular's The Perfect Road for a Square Wheel and How to Design It, describes the complete mathematical object behind my animation. My implementation reverses the presentation: the visual road remains flat while the square's center moves. I then estimate that center movement rather than solving and displaying the road.

Same mathematical problem, smaller visual requirement

A complete catenary model is valuable when the road itself is the subject. On my home page, the subject is the square reacting to your scroll. I did not throw away the mathematics; I used it to identify which properties must survive and which calculations would never become visible.

Section 06

Conclusion

In conclusion, I began with a mathematically peculiar object: a square wheel that can roll smoothly only when its road is made from repeated inverted catenaries. The complete solution moves through geometry, trigonometry, differential relationships, integration, and hyperbolic functions. However, the final object I needed was not a road or a physical simulation. I needed a square to react naturally to someone scrolling through my portfolio.

By studying the exact problem, I was able to isolate the three positions that define the visible movement and construct the unique parabola passing through them. This estimate retains 99.30 percent mean shape accuracy, never misses the exact motion by more than 1.312 percent of the total lift, and reduces the isolated calculation time by approximately one-half to two-thirds. More importantly, its worst visual error is less than a quarter of a pixel at the displayed size.

At first it may seem contradictory to use mathematics in order to create something knowingly inexact. Through this investigation I believe the opposite is true. Mathematics gave me the ability to define my acceptable error, prove the important positions remain exact, and measure what was lost between them. Without understanding the original catenary and square geometry, the parabola would be a guess. With that understanding, it becomes an optimization. The most elegant part of the implementation is not the single line of code by itself, but knowing enough about the ugly equation behind it to confidently decide which parts the browser never needed to process.

References

Sources and implementation

  1. Morphocular, “The Perfect Road for a Square Wheel and How to Design It.” Video discussion of rolling dynamics, road-wheel equations, and the catenary solution.
  2. National Museum of Mathematics, “Square-Wheeled Tricycle.” A physical realization of square wheels rolling on linked inverted catenaries.
  3. Ammons, Jacob. walkingsquare.js. Scroll sampling and parabolic center-height correction used on the home page.
  4. Ammons, Jacob. walkingsquare.css. CSS-variable translation, rotation, and responsive square sizing.

From the report back to the experiment

The square is still meant to be played with. Return to the home page, grab the scroll bar, and watch each quarter-turn repeat. What appears to be a simple decoration is a catenary problem, reduced through quadratic interpolation, running from the movement of your hand.

Return to the home page →