A zero-dependency timer that tracks real-time cost — with full state encoded in a shareable URL using Base62, integer arithmetic, and a two-layer checksum.

cost·per·time is a single-file, zero-dependency web app that runs a stopwatch or countdown timer and tracks how much money is being spent in real time. Set a rate per minute, hour, or day — and watch the cost tick up on screen. The entire timer state (mode, rate, start time, duration, label) is encoded directly into the URL, so sharing a live, synchronized timer requires nothing more than pasting a link.
This is the first project where I designed the math myself — working out the encoding scheme, the checksum layers, and the render loop from first principles.
The Algorithm — State in a URL
No server. No WebSocket. No database. Two people on opposite sides of the world open the same link and see the same timer counting in real time. Here is exactly how it works.
1. Absolute time anchoring
Instead of storing "how much time is left," the app stores a fixed UTC Unix timestamp — the moment the timer ends (countdown) or began (stopwatch).
// Encoding — when the link is generated
ts = Math.floor((Date.now() + remainingMs) / 1000) // countdown
ts = Math.floor((Date.now() - elapsedMs) / 1000) // stopwatchWhen anyone opens the link, their device computes current position from their own wall clock:
// Decoding — when the link is opened
remaining = ts * 1000 - Date.now() // countdown
elapsed = Date.now() - ts * 1000 // stopwatchEvery device already has a clock synchronized to real wall time, so the timer is automatically in sync — no server coordination required.
2. Token structure
All state is packed into a single ?d= URL parameter:
<mainSeg>[.<durSeg>][-<noteSeg>]<globalCk>| Segment | Value | Notes |
|---|---|---|
mainSeg | Base62(numStr) + innerChecksum | always present |
numStr | [mode:1][ts:10][unit:1][priceCents:N] | raw digit string before encoding |
durSeg | Base62(totalDurationSeconds) | countdown only |
noteSeg | Base62(UTF-8 bytes of label) | present if label is set |
globalCk | single Base62 checksum over entire body | tamper detection |
Example decoded numStr:
2 0001742308 2 1800
│ │ │ └─ price = $18.00 (stored as integer cents)
│ │ └──── unit index: 2 = per hour
│ └────────────── Unix timestamp (10 digits, valid until year 2286)
└────────────────── mode: 2 = countdown, 1 = stopwatch3. Base62 encoding
All numeric values are encoded in Base62 — digits + lowercase + uppercase — keeping URLs short, URL-safe, and human-copyable with no percent-encoding.
Alphabet: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
Encode: repeatedly divide by 62, map remainders to alphabet characters
Decode: treat each character as a digit in base 62, sum positionallyThe price is stored as integer cents ($12.50 → 1250) before encoding to eliminate floating-point representation issues entirely. The note/label is encoded by treating its UTF-8 byte sequence as a big-endian integer, then Base62-encoding that integer — giving full Unicode support.
4. Two-layer tamper detection
A single Base62 character checksum is derived by summing all character codes of the payload, modulo 62, then indexing into the alphabet:
const ckOf = (s) =>
B62[s.split("").reduce((a, c) => a + c.charCodeAt(0), 0) % 62];Two checksums are applied at different scopes:
| Layer | Covers | Purpose |
|---|---|---|
Inner (innerCk) | The raw numStr digits | Catches numeric-field corruption |
Global (globalCk) | Entire token body | Catches any edit anywhere in the token |
If anyone modifies the URL — even a single character — the global checksum fails and the app shows an "Invalid link" screen instead of silently running a corrupted timer.
5. Render loop
The visible timer runs on requestAnimationFrame for smooth 60fps updates with no setInterval drift:
tick(now):
elapsed = now − startTs
display = max(0, cdTotal − elapsed)
costMs = elapsed (stopwatch)
= cdTotal − display (countdown)
cost = (costMs / unitMs) × pricePerUnit
→ schedule next tickperformance.now() — monotonic and drift-free — is used inside the RAF loop. Date.now() is only used once, at link-generation time to compute the anchor timestamp.
Features
| Feature | Details |
|---|---|
| Stopwatch + countdown | Both modes; countdown stores total duration in the token |
| Real-time cost display | Updates every frame; priced per minute, hour, or day |
| Shareable synchronized links | Full state in ?d=; no server, no account, no install |
| Tamper-evident links | Two-layer Base62 checksum; corrupted links show an error screen |
| Zero infrastructure | Single index.html, ~600 lines, no dependencies |
| Offline support | Works with no network after first load |
Tech
The entire application is a single index.html file — HTML, CSS, and vanilla JavaScript, no build step, no framework, no external dependencies. Browser support: Chrome 67+, Firefox 68+, Safari 14+, Edge 79+.