Adding a play button to a PlotlyJS plot

Hi everyone,

since I got a perfect answer last time on how to add sliders to a Plotly Plot in the first place, this time I would be delighted if someone could help me edit the code below such that there is a Play Button in the Plot that automatically iterates through the different subplots (randomly generated matrices displayed as heatmaps in this case).

I know this is theoretically possible with the ‘updatemenus’ attribute but I cannot find out how to do this from the documentation provided by Plotly.

Thank you very much!

using PlotlyJS

Matrices   = [rand(2,2) for i = 1 : 3]
colorscale = [[0, "white"], [1, "cyan"]]

maps = Plot(  heatmap(z=Matrices[1], colorscale=colorscale),
              Layout(
                     width=400, 
                     height=400, 
                     margin_b=90,  
                     #this section creates the slider in the plot which can be dragged, but only manually
                     sliders= [
                            attr(
                                   active=0, 
                                   pad_t=20, 
                                   steps=[
                                          attr(
                                                 method = "restyle", 
                                                 label= "Heatmap $k",
                                                 args = [
                                                        attr(
                                                               z= (Matrices[k], ), 
                                                               colorscale=(colorscale, )
                                                        )
                                                 ] 
                                          ) for k= 1:length(Matrices)] #end steps vector
                            ) #end slider attributes
                     ] #end sliders
              ) #end layout
       ) #end plot
  
display(maps)

Until you receive a more involved response, you may be able to adapt this example from the JS version: Filled-area animation in JavaScript

1 Like

Thanks for the link, I have now tried many ideas but the only thing I get to work is the play button showing up in the plot. However it still does nothing when I click it.
In the meantime I found this example, which does not work on my machine either for some reason (I use the newest versions of vscode, julia and plotly (as of march 2023)).
Any help would be greatly appreciated! :slight_smile:

Hi, here is the pretty much exact Julia translation from the JavaScript one I linked above. Can you try this out and see if it works with your setup?

Julia version
import CSV
using DataFrames
using PlotlyJS

k_fr = 6; # initial frame to display

rows = CSV.read("finance-charts-apple.csv", DataFrame);

x = rows[:,"Date"];
yHi = rows[:,"AAPL.High"];
yLo = rows[:,"AAPL.Low"];

n = 100;

frames  = Vector{PlotlyFrame}(undef, n)
for k in 1:n
    frames[k] = frame(data=[attr(x=x[1:k], #update x and y
                                 y=yLo[1:k],
                                 ),
                            attr(x=x[1:k],
                                 y=yHi[1:k]
                                 )
                            ],
                     layout=attr(title_text="Test frame $k"),
                     name="fr$k", #frame name 
                        ) 
end

trace1 = scatter(
            mode="lines",
            name="AAPL Low",
            x=frames[k_fr].data[1].x,
            y=frames[k_fr].data[1].y,
            line=attr(color="lightgrey")
            )

trace2 = scatter(
            mode="lines",
            name="AAPL High",
            fill="tonexty",
            x=frames[k_fr].data[2].x,
            y=frames[k_fr].data[2].y,
            line=attr(color="grey")
            )


data = [trace1, trace2]

layout = Layout(
    title="Multiple Trace Filled-Area Animation",
    xaxis=attr(
      range=[frames[n].data[1].x[1], frames[n].data[1].x[n]],
      showgrid=false
    ),
    yaxis=attr(
      range=[120, 140],
      showgrid=false
    ),
    legend=attr(
      orientation="h",
      x=0.5,
      y=1.2,
      xanchor="center"
    ),
    updatemenus=[
    attr(
      x=0.5,
      y=0,
      yanchor="top",
      xanchor="center",
      showactive=false,
      direction="left",
      type="buttons",
      pad=attr(t=87, r=10),
      buttons=[attr(
        label="Play",
        method="animate",
        args=[nothing, 
            attr(
              fromcurrent=true,
              transition=(
                duration=0,
              ),
          frame=attr(
            duration=40,
            redraw=false
          )
        )],
          ),
        attr(
        label="Pause",
        method="animate",
        args=[
          [nothing],
          attr(
            mode="immediate",
            transition=attr(
              duration=0
            ),
            frame=attr(
              duration=0,
              redraw=false
            )
          )
        ],
      )]
    )]
  );

pl = Plot(data, layout, frames)

You’ll need to have added CSV.jl and DataFrames.jl for the data input step, and do this first in your working directory:

shell> curl -O https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv

or somehow download the linked CSV file.

1 Like

Here is an adaptation of your original code:

  1. Load this button attribute definition first:
Button attribute code
using PlotlyJS

DT = 1000;

buttons_attr = [attr(
        label="Play",
        method="animate",
        args=[nothing, 
            attr(
              fromcurrent=true,
              transition=(
                duration=DT,
              ),
          frame=attr(
            duration=DT,
            redraw=true
          )
        )],
          ),
        attr(
        label="Pause",
        method="animate",
        args=[
          [nothing],
          attr(
            mode="immediate",
            fromcurrent=true,
            transition=attr(
              duration=DT
            ),
            frame=attr(
              duration=DT,
              redraw=true
            )
          )
        ],
      )]
  1. The main plotting logic:
using PlotlyJS

d = 30
n = 20

#random matrices for heatmaps
Matrices   = [rand(d,d) for i = 1:n]
colorscale = [[0, "white"], [1, "cyan"]]

# create the frames for the animation
frames = PlotlyFrame[
            frame(
                data=[attr(
                        z=Matrices[k],
                        colorscale=colorscale,
                     )
                    ],
                 layout=attr(title_text="Heatmap $k"), #update title
                 name="frame_$k", #frame name; 
        ) for k in 1:n ]


#generate the Plot data 
maps = [heatmap(z=Matrices[1], colorscale=colorscale)]    
    
layout = Layout(
        width=400, 
        height=400, 
        margin_b=90,
    # add buttons to play the animation
    updatemenus=[
    attr(
      x=0.5,
      y=0,
      yanchor="top",
      xanchor="center",
      showactive=true,
      direction="left",
      type="buttons",
      pad=attr(t=87, r=10),
      buttons=buttons_attr
    )]
    ) #end layout


# display the plot data
Plot(maps, layout, frames)
1 Like

First of all thank you very much for your help and the time it must have taken you to write this and I’m sorry for not answering sooner. I have tried every combination I could think of and the buttons still do not do anything. I am using Julia version 1.8.5, PlotlyJS version 1.8.5 as well (at least that is what it says when I type “PlotlyJS.VERSION” in the Julia console), vscode version 1.76.2, Windows 11 22H2 and for viewing the plots I can also use Chrome 111.0.5563.110, in which the plot opens when I run my code in the Julia Terminal instead of inside vscode. My code now looks like this (sorry if it looks messy):

using PlotlyJS

Matrices = [rand(2, 2) for i = 1:5]
colorscale = [[0, "white"], [1, "cyan"]]

maps = Plot(
    heatmap(z = Matrices[1], colorscale = colorscale),
    Layout(
        width = 400,
        height = 400,
        margin_b = 90,
        sliders = [
            attr(
                active = 0,
                pad_t = 20,
                steps = [
                    attr(
                        method = "restyle",
                        label = "Heatmap $k",
                        args = [attr(z = (Matrices[k],), colorscale = (colorscale,))],
                    ) for k = 1:length(Matrices)
                ],
            ),
        ],
        updatemenus = [
            attr(
                type = "buttons",
                buttons = [
                    attr(
                        label = "Play",
                        method = "animate",
                        args = [
                            nothing,
                            attr(
                                fromcurrent = true,
                                transition = (duration = 0,),
                                frame = attr(duration = 1000, redraw = true),
                            ),
                        ],
                    ),
                    attr(
                        label = "Pause",
                        method = "animate",
                        args = [
                            [nothing],
                            attr(
                                mode = "immediate",
                                fromcurrent = true,
                                transition = attr(duration = 0),
                                frame = attr(duration = 1000, redraw = true),
                            ),
                        ],
                    ),
                ],
            ),
        ],
    ),
)

display(maps)

It looks like you’re missing the frames component for your example; see my example above. You need Plot(maps, layout, *frames*).

That said, there seems to be an issue in VSCode for me as well when trying to play animations; I can only get the play button to work when running through the terminal and opening in a browser.

1 Like

Thank you so much! Now I finally get it, and you’re right the buttons only work in the browser (but at least they work there). I will share my improved version later as soon as I have the time for it.

1 Like

As promised, here is now my updated code thanks to you! I also re-added the sliders in two different ways, as explained below. Also, the example here which I had mentioned above was not working, indeed does work but again not in vscode. At that time I did not yet found out that we have to paste the code into the Julia Terminal /view it in a browser.

My first code does not combine the sliders into the updatemenus attribute. That means when pressing the play button, the slider does not update its position respective to the played frame but works indepentently. The second code below combines everything.

FIRST CODE

#Code for generating a plotly plot with multiple frames.
#The individual frames can be viewed by clicking and dragging a slider or with clicking the play/pause buttons. 
#The buttons only work when viewing the plot in a browser (which will automatically open when pasting the code into the Julia Terminal).
using PlotlyJS

#data to be plotted: a vector of random matrices
Matrices = [rand(2, 2) for i = 1:100]

#define colorscheme for the plot
colorscale = [[0, "white"], [1, "cyan"]]

#generate the initial frame
maps = [heatmap(z = Matrices[1], colorscale = colorscale)]

#store all frames in a vector
frames = PlotlyFrame[
    frame(
        data = [attr(z = Matrices[k], colorscale = colorscale)],
        layout = attr(title_text = "Heatmap $k"), #update title
        name = "frame_$k", #update frame name
    ) for k = 1:length(Matrices)
]

#define the slider for manually viewing the frames
sliders_attr = [
    attr(
        active = 0,
        pad_t = 10,
        steps = [
            attr(
                method = "restyle",
                label = "Heatmap $k",
                args = [attr(z = (Matrices[k],), colorscale = (colorscale,))],
            ) for k = 1:length(Matrices)
        ],
    ),
]

#define the displaying time per played frame (in milliseconds)
dt_frame = 250

#define the play and pause buttons
buttons_attr = [
    attr(
        label = "Play",
        method = "animate",
        args = [
            nothing,
            attr(
                fromcurrent = true,
                transition = (duration = dt_frame,),
                frame = attr(duration = dt_frame, redraw = true),
            ),
        ],
    ),
    attr(
        label = "Pause",
        method = "animate",
        args = [
            [nothing],
            attr(
                mode = "immediate",
                fromcurrent = true,
                transition = attr(duration = dt_frame),
                frame = attr(duration = dt_frame, redraw = true),
            ),
        ],
    ),
]

#layout for the plot
layout = Layout(
    width = 500,
    height = 500,
    margin_b = 90,
    # add buttons to play the animation
    updatemenus = [
        attr(
            x = 0.5,
            y = 0,
            yanchor = "top",
            xanchor = "center",
            showactive = true,
            direction = "left",
            type = "buttons",
            pad = attr(t = 90, r = 10),
            buttons = buttons_attr,
        ),
    ],
    #add the sliders
    sliders = sliders_attr,
)

#save the plot and show it
plotdata = Plot(maps, layout, frames)
display(plotdata)

SECOND CODE

#Code for generating a plotly plot with multiple frames.
#The individual frames can be viewed by clicking and dragging a slider or with clicking the play/pause buttons. 
#The buttons only work when viewing the plot in a browser (which will automatically open when pasting the code into the Julia Terminal).
using PlotlyJS

#data to be plotted: a vector of random matrices
Matrices = [rand(2, 2) for i = 1:100]

#define colorscheme for the plot
colorscale = [[0, "white"], [1, "cyan"]]

#generate the initial frame
trace = [heatmap(z = Matrices[1], colorscale = colorscale)]

#store all frames in a vector
frames = PlotlyFrame[
    frame(
        data = [attr(z = Matrices[k], colorscale = colorscale)],
        layout = attr(title_text = "Heatmap $k"), #update title
        name = "frame_$k", #update frame name
        traces = [0],
    ) for k = 1:length(Matrices)
]

#define the slider for manually viewing the frames
sliders_attr = [
    attr(
        active = 0,
        minorticklen = 0,
        pad_t = 10,
        steps = [
            attr(
                method = "animate",
                label = "Heatmap $k",
                args = [
                    ["frame_$k"], #match the name of the frame again
                    attr(
                        mode = "immediate",
                        transition = attr(duration = 0),
                        frame = attr(duration = 5, redraw = true),
                    ),
                ],
            ) for k = 1:length(Matrices)
        ],
    ),
]

#define the displaying time per played frame (in milliseconds)
dt_frame = 250

#define the play and pause buttons
buttons_attr = [
    attr(
        label = "Play",
        method = "animate",
        args = [
            nothing,
            attr(
                fromcurrent = true,
                transition = (duration = dt_frame,),
                frame = attr(duration = dt_frame, redraw = true),
            ),
        ],
    ),
    attr(
        label = "Pause",
        method = "animate",
        args = [
            [nothing],
            attr(
                mode = "immediate",
                fromcurrent = true,
                transition = attr(duration = dt_frame),
                frame = attr(duration = dt_frame, redraw = true),
            ),
        ],
    ),
]

#layout for the plot
layout = Layout(
    width = 500,
    height = 500,
    margin_b = 90,
    # add buttons to play the animation
    updatemenus = [
        attr(
            x = 0.5,
            y = 0,
            yanchor = "top",
            xanchor = "center",
            showactive = true,
            direction = "left",
            type = "buttons",
            pad = attr(t = 90, r = 10),
            buttons = buttons_attr,
        ),
    ],
    #add the sliders
    sliders = sliders_attr,
)

#save the plot and show it
plotdata = Plot(trace, layout, frames)
display(plotdata)

1 Like

Well done on the second example, I couldn’t figure out how to combine both sliders and frames, and you’ve provided a nice demonstration.

The only additional suggestion I would make is to specify your colorscale bounds to stop the erratic changes between frames, by using zmax and zmin.

That is, change the line

data = [attr(z = Matrices[k], colorscale = colorscale)],

to

data = [attr(z = Matrices[k], colorscale = colorscale, zmin=0, zmax=1)],