timescale.info
Sign inSign up

How OpenChronology was built

Every merged pull request of openchronology/timeline-app, its first week of commits and its releases; and before them the 2020 PureScript editor, from snapshots of its deleted repositories in the Wayback Machine. Written to this server through the HTTP API.

327 moments · 2020-03-09T00:15:08Z CE – 2026-11-02T20:11:21Z CE · by @athan · @athan

#104 · Make navigation meet its budget: stop forcing layout while drawing

  • timeline-app
  • pull request

Closes #68. Navigation now meets its budget well past the old comfortable sizes, on every platform. The comfortable sizes rise from **1,000 to 10,000 moments** at full speed and from **500 to 1,000** on a slow device (CPU throttled 4×). In the browser and platform CI gates, no interaction is a known miss any more. ## What was slow (profiled first) I profiled wheel zooming at 4,000 moments with Chrome's CPU profiler and a trace. The drawing itself was not the main cost: - **Layout recalculation (half the time):** `width()` read `stage.clientWidth`, and `drawFrame` called it once per marker through `viewContext()` → `presented()`. Every read after the frame's DOM changes forced the browser to lay out the page again. The profile showed 12.0 s inside `width()`, against 23.7 s for the whole run. - **Style recalculation (a fifth):** `getAnimations()` and `getComputedStyle()` on labels each made the browser recompute every style first. - **Bands and arcs:** they rebuilt their text or SVG elements on every frame. Bands also printed both endpoints for their tooltips each frame, and arcs looked up both entities' names. - **Edits:** every moment edit, even typing in its title, rebuilt the duration and relationship interval trees over the whole timeline. That made Cancel and Delete miss their 100 ms budget on slow devices. ## Changes - A `ResizeObserver` keeps the stage's size. Drawing no longer reads layout. - The editor tracks the animations it starts on labels, so it never queries them. The fading time text takes its opacity from its own animation instead of `getComputedStyle`. - Relationship arcs persist by relationship and only reshape. Bands and arcs refresh their text only when the timeline's content changes. - Times parsed from frames, and groups' midpoints, are reused across frames. `Viewport.x` divides by the span as a double when the span fits comfortably in one, and falls back to exact division otherwise. - `TimelineIndex.put` and `remove` rebuild the duration and arc trees only when a moment that a duration or relationship names moves, appears or goes. A new unit test checks that the trees stay correct across each kind of edit. ## Results Medians on GitHub's runners, at full speed, in ms: | | 1,000 | 4,000 | 10,000 | 20,000 | |---|---:|---:|---:|---:| | Wheel zoom | 136 → 52 | 270 → 64 | 73 | 78 | | Drag (longest frame) | 83 → 17 | 217 → 34 | 50 | 50 | | Pinch (longest frame) | 117 → 33 | 317 → 50 | 67 | 67 | - **Throttled 4×:** wheel zoom at 1,000 moments went from 528 to 106 ms, and drag from 500 to 100 ms. The delete confirmation went from 131 to 73 ms. - **Desktop app** (software rendering under Xvfb): wheel zoom at 10,000 moments went from 666 to 98 ms. - **Platform:** at 1,000 moments, wheel zoom takes 58 ms. The full table, with the new limits, is in `docs/benchmarks.md#comfortable-sizes`. ## CI - The PostgreSQL job gates the browser and the platform at 1,000 and **10,000** moments. - **UI capacity advice** gates the slow browser at 1,000 moments with no known misses. It also requires the advice to show just past both comfortable sizes: 20,000 at full speed, and 2,000 throttled. - The desktop gate still only reports navigation and new durations, rather than gating them. Under Xvfb they render in software, which varies severalfold between runners: wheel zoom at 10,000 moments took 98 ms on one run, and keyboard zoom 525 ms on the next. WebKitWebDriver also occasionally loses a wheel or key input. Those lost inputs were hidden before as "known misses"; the reason is now stated. The capacity browser test now throttles 20×, because 1,000 moments zoom within budget at 4×. It also waits for each zoom to finish before the next. Locally, `npm test` (233 tests) and the Chromium suite pass.

Sources

Open #104 · Make navigation meet its budget: stop forcing layout while drawing in the timeline