The Fibonacci Spiral Is Secretly a Straight Line
I folded it, unfolded it, and watched it flip to the other side. A three.js experiment, the math behind it, and a few myths worth busting. You have seen the picture a thousand times: squares of size 1, 1, 2, 3, 5, 8
I folded it, unfolded it, and watched it flip to the other side. A three.js experiment, the math behind it, and a few myths worth busting.
You have seen the picture a thousand times: squares of size 1, 1, 2, 3, 5, 8 snapped together like a puzzle, with a smooth spiral curling through them.
But here's a question worth asking: what happens if you unfold it?
I hinged every square to its neighbor, pressed play, and watched the spiral untwist. The squares swung open one after another, the curve loosened, and at the end it lay perfectly flat: one straight line running through every square.
No trick. No cheating. It falls straight out of the geometry. By the end of this post, you'll know this chain of arcs can be transformed into a straight line, how to animate the fold in three.js, and which Fibonacci facts are myths.
Credit where it is due: the idea came from a much bigger animation I saw on X (link to the original post here). This is my own take on one piece of it, built from scratch in three.js.
The spiral is a chain of quarter-circles
The classic Fibonacci spiral is not one curve. It is a chain of quarter-circle arcs, one per square. Each arc has a radius equal to the side of its square, so the arcs get bigger exactly as fast as the Fibonacci numbers do.
Here is the detail that makes the whole trick work:
A quarter-circle arc inside a square connects two opposite corners of that square.
It is centered on a third, adjacent corner and sweeps across to the corner on the far side. So the straight-line shortcut between the arc's two ends, its chord, is the square's diagonal, with lengths * sqrt(2).
So what we have is a train of squares where each one has a diagonal "track" attached, and the end of one track is the start of the next.
Now imagine rotating each square around the shared point where its track meets the next. If you rotate them until every diagonal points in the same direction, the tracks line up end to end.
There is one important detail, though: rotating the squares alone does not straighten the circular arcs. The arcs must also be deformed into their chords. That is what the animation does.
The spiral is a straight line that has been folded.
A free prediction
The unfolded line has a length we can compute before running any code. Each square contributes its diagonal, so:
length = sqrt(2) * (F1 + F2 + ... + Fn)
And there is a classic identity: the sum of the first n Fibonacci numbers is F(n+2) - 1.
With 9 squares, the sizes are 1, 1, 2, 3, 5, 8, 13, 21, 34. They add up to 88, and F11 = 89, so 89 - 1 = 88. The straight line is exactly 88 * sqrt(2) units long, which you can read off from one Fibonacci number.
That's approximately 124.45 units.
This is a useful prediction because it comes entirely from the geometry. We can calculate the expected length before the animation runs, then check the result against the construction.
The twist: it folds the other way
My first version unfolded to the right and then folded back to the right, retracing its own steps. The animation that inspired me did something better: it unfolded on one side and refolded on the other.
This costs almost nothing, thanks to one more property of the squares: a square is symmetric across its own diagonal, and the chord is exactly that diagonal. Mirror a square across its chord and you get the same square back.
The trick is to let the animation continue past the straight configuration. Once an arc has been straightened to its chord, it can continue through that line and emerge as its reflection on the other side.
So the mirror-image spiral needs no new geometry. Each square keeps rotating around its hinge, twice as far as it needed to reach the straight line, and its arc flips to the other side of the chord. With a fold value t:
-
t = 0: the spiral, curling one way -
t = 1: the straight line -
t = 2: the mirror-image spiral, curling the other way
The animation loops 0 → 1 → 2 → 1 → 0, and the straight line is the hinge the whole thing swings through. Watch the slider do that, and the whole thesis lands in three seconds: the spiral on the left, the line in the middle, its mirror image on the right, all the same geometry.
Building it in three.js
Three pieces do all the work.
1. Tile the squares
First, generate the Fibonacci sequence. Then place the squares one at a time, cycling through the directions right, up, left, and down. A bounding box tracks the occupied region and determines where each new square goes. Its size comes from the next Fibonacci term.
const f = [1, 1];
while (f.length < n) {
f.push(f[f.length - 1] + f[f.length - 2]);
}
let b = { x0: 0, x1: 1, y0: 0, y1: 1 };
const dirs = ['R', 'U', 'L', 'D'];
for (let i = 1; i < n; i++) {
const s = f[i];
const d = i === 1 ? 'R' : dirs[(i - 1) % 4];
// Place the square according to d,
// then update the bounding box.
}
Each square also remembers its arc: a center corner and a start and end angle, 90 degrees apart.
2. Fold with forward kinematics
This is the part that feels like magic. Each square is a rigid body attached to the previous one at a hinge: the end of its arc. For each square, I compute how far it must turn so that its chord points along one common direction, and then I animate with a fold value t from 0 to 1.
The code below uses the same property names as the actual demo. The arrays P and v store the starting hinge and chord vector, while d stores the rotation needed to align that chord with the common direction.
let px = items[0].P[0];
let py = items[0].P[1];
items.forEach((it, i) => {
const D = t * it.d;
const c = Math.cos(D);
const s = Math.sin(D);
it.g.rotation.y = D;
it.g.position.set(px, i * 0.012, -py);
// Move the next hinge to the end of this rotated chord.
px += it.v[0] * c - it.v[1] * s;
py += it.v[0] * s + it.v[1] * c;
});
Here, P is the starting hinge point and v is the chord of the square, the same diagonal we talked about above. d is the angle each square must turn to bring its chord parallel to the common direction. The squares never separate, because every square starts exactly where the previous chord ended.
This is forward kinematics: each square's new position depends on the transformation of the previous square. Instead of moving every square independently, we propagate the hinge position along the chain.
3. Straighten the curve itself
There is one catch. If the squares just rotate, the curve stays an arc inside each square. At t = 1 the chords line up, but the curve would still wobble through each diamond.
The fix is to blend each arc point toward the matching point on its chord as t goes up, then rebuild the tube geometry every frame:
const pts = arcPoints.map((p, j) =>
p.clone().lerp(chordPoints[j], t)
);
mesh.geometry.dispose();
mesh.geometry = new THREE.TubeGeometry(
new THREE.CatmullRomCurve3(pts),
48,
radius,
6
);
At t = 0 you have the familiar spiral you came for. At t = 1 every arc lies along its chord, forming a straight line in the underlying 2D construction.
The implementation rebuilds the tube on every update. That's simple and works well for this small demo, although a larger, performance-sensitive scene could benefit from updating existing geometry instead.
Push t past 1 and the same formula overshoots: at t = 2 every point lands on the opposite side of its chord, so the arc is mirrored. That one line is the whole left-hand spiral.
One small implementation detail: the demo offsets successive square groups slightly in height to give the geometry some visual separation. If you remove that offset, the construction can lie in one plane, matching the ideal 2D geometry more closely.
Bonus: six ways to compute the numbers
The demo needs about a dozen terms, so a plain loop is plenty. If you want to explore much larger Fibonacci indices, the choice of algorithm starts to matter.
1. Naive recursion, O(φⁿ)
The version everyone writes first. It mirrors the definition perfectly and recomputes the same values again and again, so the running time grows exponentially. fib(40) is already noticeably slow.
const fib = n => (n < 2 ? n : fib(n - 1) + fib(n - 2));
2. Recursion with memoization, O(n)
Same recursion, but each result is remembered the first time it is computed, so every value is calculated once.
const memo = new Map();
function fib(n) {
if (n < 2) return n;
if (memo.has(n)) return memo.get(n);
const value = fib(n - 1) + fib(n - 2);
memo.set(n, value);
return value;
}
3. The loop, O(n)
Add the previous two terms: f[i] = f[i-1] + f[i-2]. With JavaScript Number, all Fibonacci integers through F78 are exactly representable, but F79 exceeds the safe-integer limit. Past that, use BigInt if you need exact integer results.
4. A generator (lazy stream)
Instead of building an array of a fixed size, produce the numbers one at a time and take only as many as you need. With BigInt it stays exact forever.
function* fibs() {
let [a, b] = [0n, 1n];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
const it = fibs();
const firstTen = Array.from({ length: 10 }, () => it.next().value);
5. Fast doubling, O(log n) recursive steps
Two identities let you jump from k to 2k directly: F(2k) = F(k) * (2F(k+1) - F(k)) and F(2k+1) = F(k)^2 + F(k+1)^2.
function fib(n) { // returns [F(n), F(n+1)]
if (n === 0) return [0n, 1n];
const [a, b] = fib(Math.floor(n / 2));
const c = a * (2n * b - a);
const d = a * a + b * b;
return n % 2 === 1 ? [d, c + d] : [c, d];
}
6. Binet's formula: elegant, but numerically fragile
The expression φⁿ / √5 looks like a perfect shortcut: raise the golden ratio to the nth power, divide by √5, and round to the nearest integer. But a floating-point implementation can accumulate enough error to return the wrong integer. The mathematical formula is valid; the danger is evaluating it with finite-precision arithmetic. The exact point where it fails depends on the implementation and numeric precision.
The lesson: a closed-form formula is not automatically the best algorithm. Sometimes the elegant shortcut is less reliable than a simple loop.
Bonus bonus question: why can't floats just do it? φ is irrational, so a double can only store an approximation of it. Raising that approximation to the nth power multiplies the tiny error along with the value, so the absolute error grows as fast as φⁿ does. Once it passes 0.5, rounding lands on the wrong integer. The exact route is to avoid floats entirely and do the arithmetic with integers, which is essentially what fast doubling does.
Four Fibonacci myths worth busting
Since we are here, four things people get wrong about this picture:
- It is not exactly the golden spiral. A true golden spiral grows by the factor phi (about 1.618) every quarter turn. The Fibonacci spiral only approximates that, because ratios of consecutive Fibonacci numbers get closer and closer to phi without ever hitting it. The quarter-circles also meet with matching tangents (G1 continuity), so there are no kinks. But the curvature jumps at every joint, because the radius changes abruptly from one square to the next. A true golden spiral has smoothly changing curvature.
- The nautilus shell is not a golden spiral. Its overall shape can be approximated by a logarithmic spiral, but its growth rate is not guaranteed to match the golden ratio. The familiar golden-rectangle overlay is a mathematical illustration, not evidence that the shell follows that exact construction.
- Fibonacci patterns in nature are real, but narrower than the memes suggest. The best example is plants. Sunflowers, pinecones, and pineapples often display spiral counts corresponding to consecutive Fibonacci numbers. In phyllotaxis models, these patterns are associated with growth at angles close to the golden angle, about 137.5 degrees. The biological mechanisms are more complex than a single mathematical rule, which makes the subject even more interesting.
- "Fibonacci" was not his name. The man was Leonardo of Pisa. "Fibonacci" is generally explained as a contraction of filius Bonacci, "son of Bonacci", and the nickname seems to have caught on centuries after he died. He did not discover the sequence either: Indian mathematicians had described it long before, and Leonardo brought it to Europe in his 1202 book Liber Abaci.
Try it
The full demo is in the Pen above. Drag to orbit, scroll to zoom, and use the Fold slider to scrub from the spiral (0) through the straight line (1) to its mirror image (2), and press H to hide the controls for a clean view.
Try changing the number of terms and watch the straight line get longer. Each added term adds exactly F(n) * sqrt(2) to its length.
Some ideas to take further:
- Label each square with its Fibonacci number.
- Color the squares by their ratio to the previous one and watch it settle toward phi.
- Build the sunflower version: place points at multiples of 137.5 degrees and see the spirals appear.
If you build something with this, I would love to see it. Share it in the comments.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.


