The single most important decision in Time2Bike Live Timing is:Documentation Index
Fetch the complete documentation index at: https://docs.time2.bike/llms.txt
Use this file to discover all available pages before exploring further.
Save absolute wall-clock timestamps, never “elapsed since start.”
Why
- If a
race_startevent arrives 800 ms late over the network, “elapsed” is wrong; “absolute” is still right. - Multiple phones may record events while disconnected. Reconciling by absolute time merges them deterministically.
- Dual slalom requires millisecond accuracy across devices.
Per-event fields
Each timing event in the local log (and on the server) carries:| Field | Source | Purpose |
|---|---|---|
clientEventId | crypto.randomUUID() | Idempotency key — server upserts on this. |
localTimestamp | Date.now() | Raw wall clock at button press. |
clockOffsetAtRecord | NTP-lite worker | Best estimate of device-vs-server skew. |
monotonicTimestamp | performance.now() | Drift-immune; used for live UI. |
deviceId | persisted UUID | Disambiguate parallel devices. |
userId / roleAtRecord | auth context | Audit + permission checks. |
seq | server-assigned | Monotonic per-session cursor for resume sync. |
localTimestamp − clockOffsetAtRecord.
Clock sync (NTP-lite)
A Web Worker pings/timing/clock every ~30s with a 4-timestamp exchange (t0, t1, t2, t3) and computes:
- Samples with high RTT are discarded.
- The rolling median offset across the last 7 samples is used.
- The current uncertainty is surfaced as
±N msin the operator UI.

Display precision
Each session has adisplayPrecisionMs (1, 10, 100, or 1000). Storage is always ms; display rounds to your chosen precision. Slalom usually picks 10 ms (hundredths); endurance picks 1000 ms (whole seconds).