Timegrove draws a timeline three ways. Rail is a straight vertical line with cards hanging off it; it is free, it is the default, and it is not the lesser view — it is the one that stays readable at 390 points wide with a photo on every row. Spine splits into two lanes. Vine is a stem that climbs and winds, and it is the one people screenshot.
It is also the one that took three attempts.
One path, not forty segments
The rule, before any code: the Vine is one path. A single generated curve from the root to the tip. Not a segment per event, not a repeated tile, not a border on a container. If you find yourself concatenating path strings per row, something has already gone wrong — the seams will show, and they will show worst exactly where the curve is prettiest.
What makes one path possible is a constraint on how the curve passes through each node. Every segment is a cubic bezier whose control points sit directly above and below its endpoints. That forces the tangent to be vertical at every node, which means consecutive segments meet with matching direction and the join is invisible. Get that wrong and you have a chain of arcs with a visible kink at each event, which reads as a stack of separate curves — because it is one.
The bays
The dek promises bays, so here they are. A stem that merely wiggles is decoration. This one winds because it has to make room.
Timegrove has thirteen kinds of memory, and they are not the same size. A Note or a Mood is a line of text. A Photo, a Video, an Album or a Scan is a picture that wants width. So the stem divides them by weight:
- Heavy rows — photo, video, album, scan — swing the stem far to one side so the card opens into its own bay on the other. Photo and video take the right when the stem tucks left; album and scan take the left when it swings right.
- Light rows — mood, metric, quote, milestone — do not get a card at all. They hug the stem: a leaf, a flower, a cluster of berries, drawn on the curve and rotated to its tangent at that point.
The numbers are authored against a 354-point canvas and scale with the screen: a far swing of 69, a gentle lean of 24, a bay card reaching 28 back past the centre line so it overlaps the stem rather than sitting politely beside it. Vertical values never scale — they are real points, because a row’s height comes from its content and stretching it would lie about how much is in it.
The twin problem
Here is the bug that only appears in real data.
The stem gets its rhythm from alternating: a shallow lean, then a far swing, then a crossing to the other side. That works beautifully as long as consecutive rows alternate light and heavy, which is exactly what a hand-authored design mockup does, because a designer laying out a sample tree naturally alternates for visual balance.
Real groves do not. Somebody uploads four photos from one afternoon. Now there are two heavy rows in a row, both asking for the same far swing, both landing on the same x — and the stem between two points that share an x is a straight line. The signature curve goes flat precisely at the moment the timeline gets interesting.
So a pair of same-weight rows is split by hand: the outer one takes 90, the inner one 48. And a twin pair of light rows can only be split outward, never inward, because a light node’s leaf reaches about 48 points back across the stem toward its own label — pull the node nearer the centre and the leaf lies over the words.
Taper, colour, and the two things that cannot be done the obvious way
Taper
The stem tapers from 11 pixels at the root to 3 at the tip. A single SVG path cannot do that: stroke-width is one number for the whole path. The web viewer stacks a wide path clipped to the lower half over a narrow one; the app draws it as a filled shape. Either way, taper is a thing you construct, not a property you set.
Era colour
Eras are the second one. An era in Timegrove is a span you were in — a job, a pregnancy, a year abroad — and the whole product hangs on drawing it as something parallel to the timeline rather than a band cutting across it. A band across the line says “you crossed a boundary here”. A ribbon alongside says “all of this was that”. On the Vine the stem itself changes colour for the duration, using a gradient with hard stops at each boundary expressed as a fraction of total path length. Which is only possible because it is one path with a known length — the constraint at the top of this post paying for itself.
Below 760 points it stops being a vine
None of the above survives a phone held in one hand, and pretending otherwise is how a signature view becomes an unusable one. Under 760 points of column width, Vine collapses to Rail. Not a narrower vine, not a 60/40 split — Rail.
The tempting alternative is to keep the two-lane idea and give the cards 60% of the width. At 390 points that produces cards 129 points wide, which fits a date and about four words of a caption. A layout that technically renders is not the same as a layout that works, and the honest move at that width is to admit the curve was a luxury of having room.
The same arithmetic decides whether the web viewer shows its guide column, and it comes with its own trap. The guide is 240 points wide, and whether to show it must be computed from the shell — the outer container whose width nothing on the inside can change. Compute it from the timeline column instead and you get a loop with a heartbeat: hiding the guide widens the column, the wider column re-shows the guide, which narrows the column, forever.
What we got wrong, twice
The first version drew the vine into a fixed 0–1000 coordinate space and stretched the SVG to fit the column. Non-uniform scaling made the stem measurably thicker horizontally than vertically and visibly warped every leaf. Compute in real pixels against the measured band, and never put preserveAspectRatio="none" on a vine.
The second was worse, because it did not look like a drawing bug at all: the screen went blank. Deciding the layout by measuring a row, when the layout is what sizes that row, is a loop — measure, resize, measure, resize, at frame rate, forever. Geometry comes from the container. Measure the shell, derive everything else arithmetically, and place the cards afterwards.
What it costs
Vine and Spine are Premium; Rail is free and available on every tree. The full split is on the pricing page. We are comfortable charging for this one because it is genuinely the expensive view to build and to keep correct, and because nothing about your data sits behind it — Rail shows every memory Vine shows, and the exports that carry your grove whole are free in both tiers.
You can drive all three styles on real data without installing anything: pick any of the sample groves and switch styles in the viewer. The 32 shipped groves are set up 21 on Rail, 6 on Spine and 5 on Vine, which is roughly the ratio we expect in life — Vine for the tree you show people, Rail for the ones you actually maintain.