A single URL query parameter to encode any workout timer.
?openwodtimer=emom(01:00,10)
?openwodtimer=tabata(00:20,00:10,8)
?openwodtimer=mix(emom(01:00,5),rest(02:00),tabata(00:20,00:10,8))
OpenWodTimer is an open standard that defines a compact, human-readable, composable syntax for encoding workout timer configurations in URLs. It covers For Time, AMRAP, EMOM, TABATA, RSA (Round Start At), rest periods, and mixed/composite workouts — all in a single openwodtimer query parameter.
This is a timer-only standard. It does not encode movements, exercises, or descriptions. Its sole purpose is to configure time structures that any fitness timer app can parse and run.
The formal versioned specification lives in spec/openwodtimer-v1.md. This README stays as the project overview plus a concise copy of the key rules and examples.
| Workout | URL |
|---|---|
| 20-min For Time | ?openwodtimer=ft(20:00) |
| 5-round For Time | ?openwodtimer=ft(20:00,5) |
| 15-min AMRAP | ?openwodtimer=amrap(15:00) |
| 10-min EMOM | ?openwodtimer=emom(01:00,10) |
| Classic Tabata | ?openwodtimer=tabata(00:20,00:10,8) |
| Wave starts | ?openwodtimer=rsa(00:00,02:00,04:00,06:00) |
| Competition format | ?openwodtimer=mix(ft(12:00),rest(05:00),amrap(10:00)) |
For the full normative text, see spec/openwodtimer-v1.md.
Every OpenWodTimer URL contains exactly one query parameter:
?openwodtimer=<expression>
Where <expression> is a function call: type(arg1,arg2,...). Arguments are time values (MM:SS), integers, or nested expressions.
All times use MM:SS format, zero-padded. Minutes can exceed 59 for long workouts (e.g., 90:00).
expression = workout
workout = ft | amrap | emom | tabata | rsa | mix | rest
ft = "ft(" time [ "," int ] ")"
amrap = "amrap(" time ")"
emom = "emom(" time "," int ")"
tabata = "tabata(" time "," time "," int ")"
rsa = "rsa(" time { "," time } ")"
rest = "rest(" time ")"
mix = "mix(" workout { "," workout } ")"
time = DIGIT DIGIT ":" DIGIT DIGIT
int = DIGIT { DIGIT }
DIGIT = "0" | "1" | ... | "9"
A countdown timer. Complete all work before the time cap expires.
| Argument | Position | Type | Required | Description |
|---|---|---|---|---|
| time_cap | 1 | MM:SS | Yes | Maximum time allowed |
| rounds | 2 | int | No | Number of rounds (default: 1) |
openwodtimer=ft(20:00) → 20-min cap, single round
openwodtimer=ft(20:00,5) → 20-min cap, 5 rounds
A countdown for a fixed duration.
| Argument | Position | Type | Required | Description |
|---|---|---|---|---|
| total_time | 1 | MM:SS | Yes | Total workout duration |
openwodtimer=amrap(15:00) → 15-minute AMRAP
At each interval start, perform work. Remaining time is rest. Total = interval × rounds.
| Argument | Position | Type | Required | Description |
|---|---|---|---|---|
| interval | 1 | MM:SS | Yes | Duration of each interval |
| rounds | 2 | int | Yes | Number of intervals |
openwodtimer=emom(01:00,10) → 10 rounds, 1 min each = 10 min total
openwodtimer=emom(00:30,20) → 20 rounds, 30 sec each = 10 min total
Alternating work/rest intervals. Total = (work + rest) × cycles.
| Argument | Position | Type | Required | Description |
|---|---|---|---|---|
| work | 1 | MM:SS | Yes | Work interval duration |
| rest | 2 | MM:SS | Yes | Rest interval duration |
| cycles | 3 | int | Yes | Number of work/rest pairs |
openwodtimer=tabata(00:20,00:10,8) → Classic Tabata (4 min)
openwodtimer=tabata(00:40,00:20,6) → 40/20 intervals, 6 cycles
Explicit start times for each round. Times must be ascending. Minimum 2 required.
| Argument | Position | Type | Required | Description |
|---|---|---|---|---|
| t1 | 1 | MM:SS | Yes | Start time of round 1 |
| tN | N | MM:SS | Yes | Start time of round N |
openwodtimer=rsa(00:00,02:00,04:00,06:00) → 4 rounds, uniform 2-min intervals
openwodtimer=rsa(00:00,01:30,03:30,06:00) → 4 rounds, increasing intervals
A standalone rest/countdown. Primarily used inside mix().
openwodtimer=rest(02:00) → 2-minute rest timer
Sequences multiple workout types. Nesting is allowed.
openwodtimer=mix(emom(01:00,5),tabata(00:20,00:10,8))
→ 5-min EMOM, then Tabata
openwodtimer=mix(emom(01:00,5),rest(02:00),tabata(00:20,00:10,8))
→ 5-min EMOM, 2-min rest, then Tabata
openwodtimer=mix(ft(10:00),rest(03:00),amrap(12:00))
→ For Time, rest, AMRAP
openwodtimer=mix(mix(emom(01:00,3),rest(01:00),emom(00:30,6)),rest(05:00),ft(20:00))
→ Nested: EMOM block, rest, For Time
- URL encoding: Parentheses
()and commas,do not require percent-encoding per RFC 3986, but parsers must accept both forms. - Case insensitive:
ft(),FT(),Ft()are all equivalent. - No whitespace: No spaces allowed within the expression.
- Unknown functions: Ignore for forward compatibility.
- Max nesting depth: Parsers must support at least 4 levels.
- Max URL length: 2000 characters recommended.
Implementations that serialize a parsed OpenWodTimer expression should emit a canonical form so round-tripping is stable across languages.
- Function names: Always lowercase (
ft,amrap,emom,tabata,rsa,rest,mix). - Whitespace: Emit no spaces anywhere in the expression.
- Time values: Always emit zero-padded
MM:SS. - Integers: Emit base-10 integers without a leading
+or leading zeros. - Optional FT rounds: Omit the rounds argument when its value is
1, so canonicalft(20:00,1)becomesft(20:00). - Ordering: Preserve workout order inside
mix()and timestamp order insidersa(). - Unknown functions: If ignored during parsing, they do not appear in canonical serialized output.
Examples:
FT(20:00,1) -> ft(20:00)
Mix(FT(10:00),REST(03:00),AMRAP(12:00)) -> mix(ft(10:00),rest(03:00),amrap(12:00))
| Rule | Action |
|---|---|
| Invalid MM:SS format | Reject with parsing error |
| Seconds > 59 | Reject with parsing error |
| Wrong argument count | Reject with descriptive error |
| RSA times not ascending | Reject with ordering error |
| RSA fewer than 2 times | Reject with argument count error |
| Unbalanced parentheses | Reject with syntax error |
| EMOM/TABATA rounds = 0 | Reject with range error |
| Unknown function name | Ignore (forward compatibility) |
Current version is implicitly 1. Breaking changes will use a new parameter name: opentimer2=...
| Name | Future Use |
|---|---|
countdown |
Simple countdown |
stopwatch |
Count-up with lap markers |
pyramid |
Ascending/descending intervals |
ladder |
Progressive time increases |
interval |
Generic custom intervals |
| Language | Path | Use Case |
|---|---|---|
| TypeScript | parsers/typescript/ |
Web, Node.js |
| Python | parsers/python/ |
Backend, scripting, tooling |
| Java | parsers/java/ |
JVM backend, Android-adjacent tooling |
| Kotlin | parsers/kotlin/ |
Android |
| Swift | parsers/swift/ |
iOS |
- Never combine
openwodtimerwith user IDs, auth tokens, or PII in the same URL. - Applications should preview decoded timers before starting.
- Clamp extreme values (recommended max total duration: 180 minutes).
See CONTRIBUTING.md for guidelines on proposing changes.