How to display stroke type (Breaststroke, Freestyle, Backstroke) in swimming charts?

Hello,

I have created an activity chart for my swim sessions that allows me to visualize not only the paces but also the distances and number of lengths completed for each interval, with information about distance, deviation from average pace, and number of lengths included in the tooltips that appear when hovering with the mouse.

Each bar is colored according to its pace value on the color palette. I have also added a subtle greyed-out area for each recovery or pause detected between two efforts. When hovering over this area, a tooltip displays the duration of the pause.

I would like to add the type of stroke (breaststroke, freestyle, or backstroke) detected by my Coros watch, but I don’t think this data is collected by Intervals.icu, or perhaps I don’t know the name of the variable stored in the database.

Could you help me with this?

Thank you very much.

Best regards,

1 Like

It is not implemented by default but you might be able to add it as a ā€˜Custom Stream’ if it is in the FIT file.
First check the FIT file with FitFileViewer.com to see if you can identify it.
Then in Intervals, go to Charts, Custom Streams, Add Stream and see if it is listed in the Record Field dropdown.

@MedTechCD The data is indeed present in the FIT file, but it is not listed in the Record Field dropdown

let strokeType = iv.swim_stroke || iv.customFields?.swim_stroke || "Not specified";

I tried to add code to retrieve this information, but without success.

Custom field scripts for intervals do not appear to have direct access to the ā€œlengthā€ messages in the FIT file (where swim_stroke is located).
The structure of the objects accessible within the interval script only provides access to interval properties, streams, and certain activity aggregates, but not to the raw ā€œlengthā€ messages.

You can get the message and swim stroke with that kind of code on the console:


{
for (let m of icu.fit) {
    if(m.event?.valueName === "LENGTH") {
         console.log(m); //the message
         console.log(m.swim_stroke) // swim stroke
    }
  }
}

You have to tick the ā€žProcess fit file Messageā€œ on the ā€žTypeā€œ Tab, that this works.

2 Likes

Thank you @R2Tom for this idea.
Admittedly, in the interval field editor, I can collect this data from icu.fit, but I can’t use it in my activity chart script-or at least, I don’t know how to do it.

Yeah, that’s a current limitation of the activity charts :frowning: they do not have access to the fit part of an activity. Would be great if that cloud be possible some day @david :smiley:
The only workaround I am aware of is to write the necessary parts in a custom field. From the activity chart you can then read the custom field.

I did it that way for segment laps some time ago.

1 Like

Thank you very much, @R2Tom . Your suggestion allowed me to fix the script for my activity chart and achieve the graph result I wanted.

[...]

// Update stroke style mapping according to standards
const STROKE_MAPPING = {
  "1": "Breaststroke",
  "2": "Freestyle",
  "3": "Backstroke",
  "4": "Medley",
  "undefined": "Unspecified"
};

const STROKE_COLORS = {
  "Breaststroke": "#e74c3c",
  "Freestyle": "#3498db",
  "Backstroke": "#2ecc71",
  "Medley": "#9b59b6",
  "Unspecified": "#7f8c8d"
};

 [...]

  // --- Raw SwimStroke content display ---
  console.log(`Interval ${c} - iv.customFields.SwimStroke:`, iv.customFields?.SwimStroke);

  // Detect stroke type: first from customFields, then via lengths
  let strokeValue = undefined;
  if (iv.customFields && iv.customFields.SwimStroke) {
    // Priority to customFields data if available
    strokeValue = iv.customFields.SwimStroke;
  }
 [...]

I have made it public.

I’m not a good swimmer, but I’m taking lessons with a coach :laughing::laughing:

3 Likes

I modified the code as follows.

if (typeof swimStrokeMap === "undefined") {
    // Define only if it doesn't already exist
    swimStrokeMap = {
        "0": "Freestyle",
        "1": "Backstroke", 
        "2": "Breaststroke",
        "5": "Medley"
    };
}

for (let lap of icu.fit.lap) {
    if (lap.swim_stroke !== null) {
        // Convert to string then extract
        const indexStr = lap.message_index.toString();
        const distanceStr = lap.total_distance.toString();
        const strokeStr = lap.swim_stroke.toString();
        
        // Extract numeric values
        const indexValue = indexStr.includes('=') ? indexStr.split('=')[1] : indexStr;
        const distanceValue = distanceStr.includes('=') ? distanceStr.split('=')[1].replace('(m)', '') : distanceStr;
        const strokeValue = strokeStr.includes('=') ? strokeStr.split('=')[1] : strokeStr;
        
        // Convert swim_stroke value to readable name
        const strokeName = swimStrokeMap[strokeValue] || `Type ${strokeValue}`;
        
        // Display in the requested format
        console.log(`index=${indexValue}, distance=${distanceValue}, swim_stroke=${strokeName}`);
    }
}

1 Like

Hi, I get the following error message:

Swim Interval Analysis
ReferenceError: ā€œlengthsDataā€ is not defined
Interval 1 - iv.customFields.SwimStroke: undefined

I try to debug it with my own activity, but maybe someone knows, what causes the error.

Yes everything in the fit file is only available to custom streams and activity fields with the ā€œProcesses fit file messagesā€ box ticked. And these only run when the file is re-processed, not just analysed. Intervals.icu keeps the original file in slow cloud storage, and only summary info in a local database. Currently Intervals.icu has more than 16TB of data in cloud storage. Thats way too much to keep in a local db.

That’s like just 1x Seagate IronWolf 16tb HDD

Just One :rofl::rofl::innocent::innocent:

1 Like

@Urs_Leuthold
You need to add a custom interval field, include it in the data tab, and manually select the swim stroke from the dropdown menu. Once this data is available, the script will use it to build the chart. I hope these explanations are the reason for the error.

The automatic one I made also seems to work with this chart. It does have some limitations though, as I just made it work based on my simple swims.