Skip to content

Getting Started with SWF

SWF is a JSON format for structured endurance workouts. This guide introduces one concept at a time. Every example is a complete, valid SWF file you can copy and use right away.

WorkoutSmith

You can generate SWF from natural language like "4x5min at 90% FTP with 2min rest" using WorkoutSmith, built by the team behind SWF. Currently invite-only.

Your First Workout

Step 1: The Minimal Workout

Every SWF file needs two things: a sport and some content.

{
  "sport": "cycling",
  "content": [
    { "type": "step", "effort": "rest" }
  ]
}

New here: sport and content are the only required fields. content is an array of workout elements. This workout says: easy spin, no targets.

Step 2: Adding Duration

Most steps prescribe a duration or distance. Volume values use SI units: seconds for duration, meters for distance.

{
  "sport": "running",
  "content": [
    {
      "type": "step",
      "volume": { "type": "constant", "quantity": "duration", "value": 1800 }
    }
  ]
}

New here: volume controls how long or how far. 1800 means 30 minutes (always in seconds). Use "quantity": "distance" with meters for distance-based steps.

Step 3: Adding Intensity

A step without intensity means "no specific target." Add one to prescribe how hard.

{
  "sport": "cycling",
  "content": [
    {
      "type": "step",
      "volume": { "type": "constant", "quantity": "duration", "value": 1800 },
      "intensity": { "type": "constant", "quantity": "power", "value": 200 }
    }
  ]
}

New here: intensity sets the target effort. Same structure as volume: type, quantity, value. Quantities: power (watts), speed (m/s), heart_rate (bpm), rpe_cr10, rpe_borg.

That's enough for any single-effort workout. Next up: organizing steps into phases and intervals.


Adding Structure

Step 4: Sections

Real workouts have phases. Sections group steps into warmup, main, and cooldown.

{
  "sport": "running",
  "content": [
    {
      "type": "section",
      "phase": "warmup",
      "content": [
        {
          "type": "step",
          "volume": { "type": "constant", "quantity": "duration", "value": 600 },
          "intensity": { "type": "constant", "quantity": "heart_rate", "value": 140 }
        }
      ]
    },
    {
      "type": "section",
      "phase": "main",
      "content": [
        {
          "type": "step",
          "volume": { "type": "constant", "quantity": "duration", "value": 1200 },
          "intensity": { "type": "constant", "quantity": "heart_rate", "value": 165 }
        }
      ]
    },
    {
      "type": "section",
      "phase": "cooldown",
      "content": [
        {
          "type": "step",
          "volume": { "type": "constant", "quantity": "duration", "value": 300 },
          "effort": "rest"
        }
      ]
    }
  ]
}

New here: section groups steps into a phase: "warmup", "main", or "cooldown". Each section has its own content array.

Step 5: Repeats

Intervals are the backbone of structured training. A repeat loops its content a fixed number of times.

{
  "sport": "cycling",
  "content": [
    {
      "type": "section",
      "phase": "warmup",
      "content": [
        {
          "type": "step",
          "volume": { "type": "constant", "quantity": "duration", "value": 600 }
        }
      ]
    },
    {
      "type": "section",
      "phase": "main",
      "content": [
        {
          "type": "repeat",
          "count": 4,
          "content": [
            {
              "type": "step",
              "volume": { "type": "constant", "quantity": "duration", "value": 300 },
              "intensity": { "type": "constant", "quantity": "power", "value": 250 }
            },
            {
              "type": "step",
              "effort": "rest",
              "volume": { "type": "constant", "quantity": "duration", "value": 120 }
            }
          ]
        }
      ]
    },
    {
      "type": "section",
      "phase": "cooldown",
      "content": [
        {
          "type": "step",
          "volume": { "type": "constant", "quantity": "duration", "value": 300 }
        }
      ]
    }
  ]
}

New here: repeat loops its content array count times. This workout does 4 x (5min at 250W + 2min rest).

This covers most structured workouts. The rest of the guide is about targeting intensity in more useful ways than a single number.


Intensity Shapes

So far, every intensity has been a fixed number (constant). SWF has three more shapes for specifying targets.

Step 6: Ranges and Ramps

A range gives a target band. A ramp builds linearly from one value to another.

{
  "sport": "cycling",
  "content": [
    {
      "type": "section",
      "phase": "warmup",
      "content": [
        {
          "type": "step",
          "volume": { "type": "constant", "quantity": "duration", "value": 600 },
          "intensity": { "type": "ramp", "quantity": "power", "start": 120, "end": 200 }
        }
      ]
    },
    {
      "type": "section",
      "phase": "main",
      "content": [
        {
          "type": "step",
          "volume": { "type": "constant", "quantity": "duration", "value": 1200 },
          "intensity": { "type": "range", "quantity": "power", "min": 220, "max": 250 }
        }
      ]
    }
  ]
}

New here: Two more intensity shapes:

Shape Fields Meaning
constant value Hit this number
range min, max Stay in this band
ramp start, end Build from A to B

Step 7: Parameter References

Hard-coded power targets only work for one athlete. Replace absolute values with percentages of a parameter, and the workout becomes a reusable template.

{
  "sport": "cycling",
  "content": [
    {
      "type": "step",
      "volume": { "type": "constant", "quantity": "duration", "value": 1200 },
      "intensity": { "type": "range", "quantity": "power",
        "min": { "percent": 88, "of": "ftp" },
        "max": { "percent": 93, "of": "ftp" }
      }
    }
  ]
}

New here: Instead of "value": 250, write { "percent": 88, "of": "ftp" }. Supply the athlete's FTP at export time and the percentages resolve to watts. Parameter names (ftp, lthr, max_hr, ...) are open strings, not a fixed enum.

Step 8: Zones

When you don't need exact numbers, reference the athlete's device zones directly. SWF supports 3, 5, and 7-zone models.

{
  "sport": "cycling",
  "content": [
    {
      "type": "step",
      "volume": { "type": "constant", "quantity": "duration", "value": 3600 },
      "intensity": { "type": "zone", "quantity": "power", "zone": 2, "of": 5 }
    }
  ]
}

New here: zone references the athlete's device zones. "zone": 2, "of": 5 means zone 2 in a 5-zone model. "of" declares the zone model: 3, 5, or 7 zones.


Metadata and Notes

Step 9: Title and Description

Add title and description at the top level to label your workouts.

{
  "sport": "cycling",
  "title": "Sweet Spot 2x20",
  "description": "2x20min at 88-93% FTP with 5min recovery between intervals.",
  "content": [
    {
      "type": "step",
      "volume": { "type": "constant", "quantity": "duration", "value": 1200 },
      "intensity": { "type": "range", "quantity": "power",
        "min": { "percent": 88, "of": "ftp" },
        "max": { "percent": 93, "of": "ftp" }
      }
    }
  ]
}

New here: title and description are optional top-level fields. Most export targets use the title as the workout name.

Step 10: Notes

Notes attach coaching cues to your workout. They can appear standalone in content arrays, or inline on steps, sections, and repeats via the note field.

{
  "sport": "running",
  "content": [
    { "type": "note", "text": "Focus on cadence today, aim for 180spm." },
    {
      "type": "step",
      "note": "Start easy, find your rhythm.",
      "volume": { "type": "constant", "quantity": "duration", "value": 600 }
    }
  ]
}

New here: Two ways to use notes. A "type": "note" element is standalone text in the content array. A "note" field on a step (or section, or repeat) attaches a cue directly to that element.


Next Steps

That covers all content types (step, repeat, section, note), all four intensity shapes, and workout metadata. See the specification for validation rules and edge cases.