6 min read

Building Untangle: What Simulating Real Ropes Taught Me About Puzzle Design

Planarity is an old genre. Older than the App Store. You get a tangle of lines and dots, you drag the dots around until no line crosses another, and the graph untangles. Every implementation I have ever played, going back to the Flash era, uses the same shortcut: the lines are rigid. Straight segments with no weight, no slack, no memory of being tangled.

My version, Untangle, ships with actual rope simulation. Ropes that sag, coil, swing when you let go, and get in your own way. This is a writeup of what that decision cost me and what it gave back, because the two lists turned out to be longer than I expected.

The shortcut every clone takes

With rigid segments, the puzzle is pure graph theory. Crossing detection is a 2D segment intersection test, the board state fits in a struct, and "solved" is just a boolean over all pairs. You can generate a thousand levels procedurally by scattering points and connecting them, and every one of them is technically solvable. It is clean, it is cheap, and after about four levels it starts to feel like homework.

I built one of these first. Prototype to App Store preview in a weekend. Then I put it in front of people and watched a tester solve two levels with the exact same three moves, sigh, and close the app. The puzzle was correct and it was dead.

Ropes, but cheap enough for a phone

The fix was Verlet integration, the same position-based trick Half-Life 2 used for ragdolls and every cloth demo since. Each rope is a chain of particles. Every frame you apply gravity, then run a few constraint passes that pull neighboring particles back toward their rest distance. No forces, no derivatives, no instability spiral. The whole loop is about twenty lines:

for i in 1..<points.count {
    let p = points[i]
    let v = (p.pos - p.old) * damping
    p.old = p.pos
    p.pos += v + gravity * dt
}

for _ in 0..<passes {
    for i in 1..<points.count {
        let a = points[i - 1], b = points[i]
        let d = b.pos - a.pos
        let diff = (d.length - restLength) / d.length
        a.pos += d * 0.5 * diff
        b.pos -= d * 0.5 * diff
    }
    // pin the anchored ends
}

The numbers that matter: 14 particles per rope, 8 constraint passes, 6 to 10 ropes on screen. That is roughly 1,100 particles plus segment-segment intersection tests every frame, and it holds 120fps on a three-year-old iPhone. I spent two full days on a spatial hash for the intersection tests before admitting brute force over sorted segments was faster at this scale. Classic.

What physics actually changes

I expected prettier visuals. I did not expect the puzzle to change.

With rigid lines, dragging an endpoint can never make things worse in a new way. The graph is the graph. With rope, slack becomes a resource. Pull an endpoint across the board and the rope trails behind you, picks up crossings you did not intend, and drags them back when you release. Players started doing something I never designed: instead of dragging knots apart, they gather the slack, shake it, and let the rope's own tension do the untangling. It looks wrong. It is deeply satisfying. It is now the intended strategy, because four of my first five testers found it before I did.

It also creates failure modes that do not exist in the rigid version. A rope can loop around an anchor point and lock. Two coils can settle into a configuration that looks unsolvable and technically is not, but only a masochist would confirm that by hand. Every level in the final game went through a pass where I tried to be an idiot on purpose, because players are wonderfully creative idiots and the simulation honors their creativity whether I like it or not.

Why the levels are handcrafted

My procedural generator produced solvable knots with a controllable crossing count. It also produced garbage. The crossing count does not capture difficulty, not even close. What makes a knot hard is where the slack lives, whether the anchored endpoints fight each other, and how much of the tangle resolves itself once you find the one key move. These are properties nobody knows how to measure, so I stopped measuring and started handcrafting.

All 50 levels are built by hand in a tool I wrote for the purpose, in difficulty order. Level 9 teaches you that slack is movable. Level 14 punishes you for hoarding it. Level 17 is the one I rebuilt forty-some times; it looks like a flower and plays like a trap, and I can still solve it in eleven seconds because I have done it maybe a thousand times. Watching someone else solve it for the first time remains the most fun I have had with this project.

The feedback loop

Crossing detection drives everything, so I spent real effort on how crossings are communicated. Every intersection gets a live marker the moment it appears. No subtlety, no judgment call about whether lines "count as" touching. When the last marker clears, the ropes settle into the hidden shape and the level snaps to its reveal. That moment of the pattern appearing out of a knot is the reason the game exists, and it is the one thing I refused to compromise on while everything else got cut.

Around the core there is a daily challenge with a shared seed, so everyone untangles the same knot on the same day. There is a zen mode with no markers for people who want it harder. And there is a level editor, which started as a favor to one beta tester and turned out to be how I validate new puzzles without burning a week of my own playtesting.

Pricing, because someone will ask

$1.99, once. All 50 levels, the daily challenge, the editor, no ads, no subscriptions, no analytics. It works offline because rope simulation is just arithmetic, and there is no server to pay for. This is the same bet I made with my other apps and I keep making it even though every dashboards-and-funnels post says I am leaving money on the table. Maybe I am.

One thing I still have not resolved. Some testers told me they preferred my first prototype, the rigid-line version, for its austerity. Fewer toys, purer logic. I think they are wrong and also I cannot fully prove it, and that argument with myself has been running for months. The physics version has retention the flat one never approached, so Untangle ships with ropes. But if you are building a planarity-style game and you want the calm, mathematical version, that game is still sitting in a drawer here and it is good too.