Custom activity charts

You can now add custom activity charts to the activity heart rate, power, pace and data pages. These charts are created using Javascript code executed on the servers and rendered using Plotly.

Look for the new charts button at the bottom of the activity power/hr/pace/data pages:

You can toggle all the standard charts on/off and move them around and add your own charts. You can optionally share your own charts with everyone and search for charts others have created.

The selected charts for each page are stored per sport.

The script has access to an “icu” object providing access to the following objects:

  • activity
  • athlete
  • streams (watts etc.)
  • sportSettings (for the activity sport)
  • wellness (wellness record for the day)
  • powerCurve (power duration curve)
  • powerCurveFatigued0 (power curve after some KJ of work done)
  • powerCurveFatigued1 (power curve after more KJ of work done)
  • hrCurve (heart rate duration curve)
  • paceCurve (distance vs time curve)
  • gapCurve (distance vs time using gradient adjusted pace)

Here is the code:

  let activity = icu.activity

  let x = []
  let y = []
  let intervals = activity.icu_intervals || []
  for (let i = 0, c = 0; i < intervals.length; i++) {
    let iv = intervals[i]
    if (iv.type !== 'WORK') continue
    x.push(iv.label || c)

  let data = [
      x: x,
      y: y,
      type: 'bar',
      marker: {
        color: '#63c',
        opacity: 0.7

  let layout = {
    title: {
      text: "Average power for work intervals"
    margin: {
      l: 30,
      r: 20,
      t: 20,
      b: 30

  chart = { data, layout }

Awesome! Time to play!

Just trying and already broke it :grinning_face_with_smiling_eyes:

Activity i12226119

Edit: ok, my bad. This happens when having intervals with same name. I should do an average of the data on the same interval.
Edit 2: or I can have it per interval just adding the index: x.push(iv.label ? c + '-' + iv.label : c)

Ok, one try with pace:

activity = icu.activity

x = []
y = []
p = []
intervals = activity.icu_intervals || []
for (let i = 0, c = 0; i < intervals.length; i++) {
  let iv = intervals[i]
  if (iv.type !== 'WORK') continue
  x.push(iv.label ? c + '-' + iv.label : c)
  pace = 60 / (iv.average_speed * 3.6)
  min = Math.floor(pace)
  secs = Math.round((pace - min) * 60, 2)
  p.push(min + ':' + ('0' + secs).slice(-2))

data = [
    x: x,
    y: y,
    text: p,
    type: 'bar',
    marker: {
      color: 'DodgerBlue'

layout = {
  title: {
    text: "Average pace for work intervals"
  yaxis: {
     range: [Math.min(...y) - 0.5, Math.max(...y)]

chart = { data, layout }

I don’t know how to only show pace as pace formatted. Also could be nice to show larger bars for more speed intervals (lower is better).

Thank you @david for this
Can we use any feature provided by Plotly to create these chart? I mean, different kind of charts (scatter, line, …), several traces in the same chart, etc

This is very cool and I’m certainly getting ahead of myself but would love to use sliders too

First attempt using it and I must thank again for your work @david


Jajajajajajajajjajajaa, anda que te has aguantado en hacerlo

@david will these charts be available in the mobile intervals app?

Loving having a play with this

One bug I have found is that the intervals’ watts seem to fail when you give it a label

Yes they do work on mobile.

Yes anything you can create with Plotly should work. I am not familiar with Plotly myself. I picked it because it is declarative and seemed full featured + there are versions for Python and so on so others might know it.

I probably need to add some formatting functions to the environment for pace and speed.

1 Like

Wow, already looks very cool! Are you planning to make that one public?

Never mind, actually fairly simple… I’ve shared a chart as a demo

Joder! Esto como se hace?

This was a very quick work for testing. I will share a completely finished one


Great, I have a question, could iv.moving_time be used to remove the pause time from the average of a lap in a graph?
if ( iv.moving_time != ’ ? ')

I added support for uploading screenshots for custom activity fields. If you are creating a new one you do need to save it first, then re-open to see the screenshot tab.


is it possible from the script to enlarge the graph? on the phone it’s a small

I haven’t quite figured it out completely and on mobile it seems to act a bit strange but there are sizing options on the third tab when you click on the pencil icon for the custom chart.

You can use px (pixels), % and it does something without units too. Don’t know what…

You can also zoom in by drawing a rectangle on the place of intrest. Double tap to zoom out again. The 2 finger zoom doesn’t seem to work here.

Maybe some of you can help me, I think there is no way to access the other sections of the ‘.fit’ file, right ?

I wanted to try implement a recap graph for weight training session. Showing the rest/work graph for different sets. The weight training files are a bit different from the usual bike ride ‘.fit’ files, the reps logs are stored in a section called “set” (see the screen shot from I didn’t found any way to access such sections from javascript code.

You can access with the code “set.weight”, but you only get the value of the last row.

However, you can only map FIT file fields to custom activity fields, so you will only see them as summary. If you only want it per session and your last “set” row has the final weight you want, then you are good to go with “set.weight” as the FIT file field.