Rene Saarsoo bio photo

Rene Saarsoo

Väike alatu progeja.

LinkedIn Github

Why the ZWO format sucks

ZWO is a workout file format used by Zwift, and it’s really really bad. Basically it’s a showcase of all the things not to do when designing a file format.

Here’s an example of a ZWO file:

<workout_file>
<name>SST (short)</name>
<author></author>
<description>Sweet Spot Training will give great bang for buck.</description>
<sportType>bike</sportType>
<workout>
<Warmup Duration="300" PowerLow="0.30000001" PowerHigh="0.69999999">
<textevent timeoffset="20" message="Welcome to SST workout"/>
<textevent timeoffset="30" message="Time to get warmed up"/>
<textevent timeoffset="120" message="get your cadence up to 90-100rpm" y="270"/>
</Warmup>
<IntervalsT Repeat="4" OnDuration="300" OnPower="0.96" OffDuration="300" OffPower="0.88">
</IntervalsT>
<Cooldown Duration="300" PowerLow="0.5" PowerHigh="0.30000001"/>
</workout>
</workout_file>

Inconsistencies

The most basic inconsistency is in how names are written:

  • Element names are sometimes in lowercase (e.f. <textevent>), sometimes in snake case (e.g. <workout_file>), sometimes camel case (e.g. <sportType>), sometimes start case (e.g. <SteadyState>).
  • Similarly with attributes: PowerLow= v/s timeoffset=.

Then there are inconcistencies of forming names:

  • In ramps a suffix is used: PowerLow and PowerHigh, but in intervals a prefix: OnPower and OffPower.
  • The On and Off prefixes are used for both Power (OnPower / OffPower) and Duration (OnDuration / OffDuration), but not for Cadence (Cadence / CadenceResting).

Finally, specific to XML, the free-form text is sometimes as a content and sometimes as attribute value:

  • As content: <description>Some description here</description>
  • As attribute: <textevent message="Your message here" />

Misleading names

The previous things were just the surface. Often the names are misleading or just plain weird:

  • <IntervalsT> - what does this T mean? No idea.
  • Power="1.00" - it’s not power (in watts) at all, it’s an intensity value.
  • Duration="200" - it might not be a duration at all, instead it might be a distance.
  • PowerLow might contain higher value than PowerHigh.

Coupled with implementation

Looks like lots of the values in the XML come straight out of the Zwift app, in the way that just happened to be most convenient for developers.

The worst of these is the pace field which contains a number from 1 to 5, with the following meanings:

  • 1 - 1 mile pace
  • 2 - 5 km pace
  • 3 - 10 km pace
  • 4 - half marathon pace
  • 5 - marathon pace

That’s just bad in so many ways:

  • it’s impossible to understand the meaning of these values from XML file alone.
  • the relationship between the values and their meaning is completely arbitrary.
  • Currently they’re nicely ordered by the distance, but what if you want to extend this list in the future with 1000 meters pace?

The values coming out of Zwift’s own workout editor are also fun:

  • Power=“0.89999998“
  • Duration=“180.00002“

They clearly want you to be very exact and exercise precisely at 89.999998% of FTP for 3 minutes and 20 microseconds :) Or was it 180 meters and 20 micrometers?

So…?

Because of all these problems and several others, authoring a ZWO file manually is quite some pain. That’s why I created my own workout file format and a tool (available as online editor) for converting it to ZWO file, which Zwift could understand.