Building the Subtask Graph
What if you could see the flow of your tasks? The story of building an interactive graph editor — and stumbling through it.
Building the Subtask Graph
After adding subtasks, I felt pretty good for a while. You could break big tasks into smaller pieces and knock them out one by one.
But the more I used it, the more something bugged me. These subtasks had an order to them — and a flat list couldn’t show it.
The Limits of a List
Three subtasks? A list is fine. Just go top to bottom.
But at five or ten, things get tangled. Some can run in parallel. Some need a predecessor to finish first. Sometimes you have to pick one path or the other. Lining them all up vertically hid those relationships completely.
So I decided to try building a graph. Honestly, I wasn’t sure I could pull it off.
The Basics Came Together in a Day
Surprisingly — I was the most surprised — the skeleton took just one day. One subtask becomes one rectangular card, one connection becomes one bezier curve. Drag to connect, drag to move. About 1,700 lines.
There was a moment of “wait, this actually works?” That felt really good.
But that was just the beginning.
The Layout Problem
When nodes first appear on screen, where should they go? New nodes that have never been positioned need to land somewhere sensible on their own.
I tried topological sorting — starting from nodes with no incoming edges, grouping them into layers. The theory made sense, but the edge cases in practice were endless.
I ended up compromising: respect user-positioned nodes, auto-place only the new ones. Not perfect, but usable. Probably.
Rotation Was Harder Than I Thought
Landscape mode felt necessary for graphs on mobile. More nodes means more room needed. “Just rotate it,” I thought casually.
Wrong.
Rotating 90 degrees means a finger’s X-axis movement maps to the canvas Y-axis. You can’t just CSS-rotate and call it done — gesture coordinates need transforming. Finding these two lines took an embarrassingly long time:
dx = rotated ? translationY : translationX
dy = rotated ? -translationX : translationY
A two-line solution that I’m a little ashamed took so long to reach. But I’m writing it down honestly.
Solid and Dashed
While building, I realized not all connections are the same. There’s the “finish this, then do that” mandatory path, and the “you could try this instead” alternative path.
So I split them: solid lines for primary, dashed for alternative. Tap to switch, set priorities. One rule: if a node has only one outgoing connection, you can’t make it an alternative. Calling the only way forward “optional” would be strange.
Fingers Are Not Precise
Tapping a 2-pixel-wide line on a phone is nearly impossible. I couldn’t do it myself. If the person who built it can’t tap it, something’s wrong.
So I layered invisible 20-pixel touch targets on top of each edge. Your eyes see a thin line, your finger hits something generous. Node handles got 8 extra pixels too.
This kind of invisible work ate the most time. Nobody notices it, but everyone notices when it’s missing.
Three Weeks
Started February 17, wrapped up around March 8. The first day gave me the basics. The remaining twenty days were all polish.
Whether I built it well — I honestly don’t know. But it doesn’t flicker when you drag, it snaps when you connect, and your finger hits what it’s aiming for. I think I got at least that much right. Probably.
There are surely parts that could be better. If something feels off when you use it, please let me know.
I hope that seeing the flow of your tasks makes finishing them a little easier.