Skip to main content

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.

The single most important decision in Time2Bike Live Timing is:
Save absolute wall-clock timestamps, never “elapsed since start.”

Why

  • If a race_start event 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:
FieldSourcePurpose
clientEventIdcrypto.randomUUID()Idempotency key — server upserts on this.
localTimestampDate.now()Raw wall clock at button press.
clockOffsetAtRecordNTP-lite workerBest estimate of device-vs-server skew.
monotonicTimestampperformance.now()Drift-immune; used for live UI.
deviceIdpersisted UUIDDisambiguate parallel devices.
userId / roleAtRecordauth contextAudit + permission checks.
seqserver-assignedMonotonic per-session cursor for resume sync.
The server’s “truth” timestamp is 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:
offset = ((t1 - t0) + (t2 - t3)) / 2
rtt    =  (t3 - t0) - (t2 - t1)
  • Samples with high RTT are discarded.
  • The rolling median offset across the last 7 samples is used.
  • The current uncertainty is surfaced as ±N ms in the operator UI.
This is good for reliable millisecond accuracy on a typical race-day Wi-Fi or LTE network — exactly what dual slalom needs. Time sync

Display precision

Each session has a displayPrecisionMs (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).