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.
New here:
sportandcontentare the only required fields.contentis 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:
volumecontrols how long or how far.1800means 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:
intensitysets 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:
sectiongroups steps into aphase:"warmup","main", or"cooldown". Each section has its owncontentarray.
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:
repeatloops itscontentarraycounttimes. 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 constantvalueHit this number rangemin,maxStay in this band rampstart,endBuild 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:
zonereferences the athlete's device zones."zone": 2, "of": 5means zone 2 in a 5-zone model."of"declares the zone model:3,5, or7zones.
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:
titleanddescriptionare 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.
- Specification - full field reference and validation rules
- JSON Schema - editor autocomplete and validation
- Conformance suite - valid and invalid examples for testing
- Python library - build, validate, and export workouts
- Playground - try SWF in your browser