# Slow makie animations?

**URL:** <https://discourse.julialang.org/t/slow-makie-animations/103626>\
**Category:** Performance\
**Created:** [September 7, 2023, 5:45pm UTC](https://discourse.julialang.org/t/slow-makie-animations/103626 "2023-09-07T17:45:44Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![rkube](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rkube/32/211198_2.png) [@rkube](https://discourse.julialang.org/u/rkube)\
**Post date:** [September 7, 2023, 5:45pm UTC](https://discourse.julialang.org/t/slow-makie-animations/103626/1 "2023-09-07T17:45:44Z")

</div>

Hi,  
I’m trying to animate some makie plots and performance seems to be a bit lacking.  
Animating this plot takes about 47 seconds on my machine  
`47.157643 seconds (30.39 M allocations: 3.480 GiB, 1.38% gc time, 3.92% compilation time)`

This is the animation script.

```julia
using CairoMakie

function draw_me(N, Z, n_frames)
    tb = 1:N

    tr_1 = randn(N)
    tr_2 = randn(N, Z, n_frames)

    # Observables give the x-coordinate of the vertical time bar
    ix_t = Observable(1)

    timebar_x = @lift [tb[$ix_t], tb[$ix_t]]
    current_frame = @lift tr_2[:, :, $ix_t]

    fig = Figure()
    ax_l = Axis(fig[1, 1])
    ax_c = Axis(fig[1, 2])

    lines!(ax_l, tb, tr_1)
    lines!(ax_l, timebar_x, [-2, 2], color=:red, linewidth=3)
    contourf!(ax_c, tb, 1:Z, current_frame)

    @time record(fig, "test.mp4", 1:n_frames,
        framerate=5) do t
        ix_t[] = t
    end

end

draw_me(100, 30, 30)

```

I’m also seeing only 100% CPU utilization, is there maybe a way to parallelize this?

[https://global.discourse-cdn.com/julialang/original/3X/d/5/d5e35753e9c3922084aa0366f427dfd1ab5c4b23.mp4](https://global.discourse-cdn.com/julialang/original/3X/d/5/d5e35753e9c3922084aa0366f427dfd1ab5c4b23.mp4)

---

<div class="post-metadata">

**Author:** ![sdanisch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sdanisch/32/1406_2.png) [@sdanisch](https://discourse.julialang.org/u/sdanisch)\
**Post date:** [September 7, 2023, 6:38pm UTC](https://discourse.julialang.org/t/slow-makie-animations/103626/2 "2023-09-07T18:38:36Z")

</div>

Contourf is pretty slow… How much faster is it if you do the animation without it?

---

<div class="post-metadata">

**Author:** ![rkube](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rkube/32/211198_2.png) [@rkube](https://discourse.julialang.org/u/rkube)\
**Post date:** [September 7, 2023, 6:43pm UTC](https://discourse.julialang.org/t/slow-makie-animations/103626/3 "2023-09-07T18:43:54Z")

</div>

Without the contour plot:

```julia
 1.677212 seconds (1.41 M allocations: 96.226 MiB, 4.43% gc time, 75.97% compilation time)

```

---

<div class="post-metadata">

**Author:** ![\_bernhard](https://avatars.discourse-cdn.com/v4/letter/_/bc79bd/32.png) [@\_bernhard](https://discourse.julialang.org/u/_bernhard)\
**Post date:** [September 7, 2023, 6:53pm UTC](https://discourse.julialang.org/t/slow-makie-animations/103626/4 "2023-09-07T18:53:05Z")

</div>

Maybe try using the lower-level `recordframe!` interface?

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [September 7, 2023, 6:55pm UTC](https://discourse.julialang.org/t/slow-makie-animations/103626/5 "2023-09-07T18:55:08Z")

</div>

Random matrices are very taxing with contourf because they lead to a lot of tiny polygons. If you use more structured data, is it faster?

---

<div class="post-metadata">

**Author:** ![rkube](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rkube/32/211198_2.png) [@rkube](https://discourse.julialang.org/u/rkube)\
**Post date:** [September 7, 2023, 6:56pm UTC](https://discourse.julialang.org/t/slow-makie-animations/103626/6 "2023-09-07T18:56:10Z")

</div>

Saving the files instead of animating is about 50% faster.

```julia
function draw_me_save(N, Z, n_frames)
    tb = 1:N

    tr_1 = randn(N)
    tr_2 = randn(N, Z, n_frames)

    for ix_f ∈ 1:n_frames
        timebar_x = [tb[ix_f], tb[ix_f]]
        current_frame = tr_2[:, :, ix_f]

        fig = Figure()
        ax_l = Axis(fig[1, 1])
        ax_c = Axis(fig[1, 2])

        lines!(ax_l, tb, tr_1)
        lines!(ax_l, timebar_x, [-2, 2], color=:red, linewidth=3)
        contourf!(ax_c, tb, 1:Z, current_frame)

        save("test_frame_" * lpad(ix_f, 2, "0") * ".png", fig)
    end
end

@time draw_me_save(100, 30, 30)

```

` 22.918960 seconds (47.75 M allocations: 2.560 GiB, 3.02% gc time, 0.05% compilation time)`

Followed by `~2s` for ffmpeg:

```julia
time ffmpeg -framerate 5 -pattern_type glob -i 'test_frame_*.png' -c:v libx264 -pix_fmt yuv420p out.mp4

```

---

<div class="post-metadata">

**Author:** ![rkube](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rkube/32/211198_2.png) [@rkube](https://discourse.julialang.org/u/rkube)\
**Post date:** [September 7, 2023, 6:57pm UTC](https://discourse.julialang.org/t/slow-makie-animations/103626/7 "2023-09-07T18:57:03Z")

</div>

This is code is an MWE for my data that is more regular. Performance is bad there too.

---

<div class="post-metadata">

**Author:** ![rkube](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rkube/32/211198_2.png) [@rkube](https://discourse.julialang.org/u/rkube)\
**Post date:** [September 7, 2023, 7:18pm UTC](https://discourse.julialang.org/t/slow-makie-animations/103626/8 "2023-09-07T19:18:32Z")

</div>

Using `recordframe!` gives about the same performance as plotting each frame individually, saving to png, and ffmpeg-ing it:

```julia
using CairoMakie

function draw_me(N, Z, n_frames)
    tb = 1:N

    tr_1 = randn(N)
    tr_2 = randn(N, Z, n_frames)

    # Observables give the x-coordinate of the vertical time bar
    ix_t = Observable(1)

    timebar_x = @lift [tb[$ix_t], tb[$ix_t]]
    current_frame = @lift tr_2[:, :, $ix_t]

    fig = Figure()
    ax_l = Axis(fig[1, 1])
    ax_c = Axis(fig[1, 2])

    lines!(ax_l, tb, tr_1)
    lines!(ax_l, timebar_x, [-2, 2], color=:red, linewidth=3)
    contourf!(ax_c, tb, 1:Z, current_frame)

    record(fig, "test.mp4", 1:n_frames,
        framerate=5) do t
        ix_t[] = t
    end
end

function draw_me_save(N, Z, n_frames)
    tb = 1:N

    tr_1 = randn(N)
    tr_2 = randn(N, Z, n_frames)

    for ix_f ∈ 1:n_frames
        timebar_x = [tb[ix_f], tb[ix_f]]
        current_frame = tr_2[:, :, ix_f]

        fig = Figure()
        ax_l = Axis(fig[1, 1])
        ax_c = Axis(fig[1, 2])

        lines!(ax_l, tb, tr_1)
        lines!(ax_l, timebar_x, [-2, 2], color=:red, linewidth=3)
        contourf!(ax_c, tb, 1:Z, current_frame)

        save("test_frame_" * lpad(ix_f, 2, "0") * ".png", fig)
    end
    println("To make movie execute:")
    println("ffmpeg -framerate 5 -pattern_type glob -i 'test_frame_*.png' -c:v libx264 -pix_fmt yuv420p out.mp4")
end

function draw_me_lowlevel(N, Z, n_frames)
    tb = 1:N

    tr_1 = randn(N)
    tr_2 = randn(N, Z, n_frames)

    fig = Figure()
    ax_l = Axis(fig[1, 1])
    ax_c = Axis(fig[1, 2])

    l1 = lines!(ax_l, tb, tr_1)   

    record(fig, "test_low.mp4") do io
        for ix_t ∈ 1:n_frames
            timebar_x = [tb[ix_t], tb[ix_t]]
            current_frame = tr_2[:, :, ix_t]

            l2 = lines!(ax_l, timebar_x, [-2, 2], color=:red, linewidth=3)
            c = contourf!(ax_c, tb, 1:Z, current_frame)

            recordframe!(io)

            delete!(ax_l, l2)
            delete!(ax_c, c)
        end
    end
end

println("Low-level interface")
@time draw_me_lowlevel(100, 30, 30)
println("Writing frames as png")
@time draw_me_save(100, 30, 30)
println("Observables and @lift macros")
@time draw_me(100, 30, 30)

```

gives me

```julia
Testing low-level
 26.291187 seconds (36.92 M allocations: 2.240 GiB, 2.55% gc time, 23.76% compilation time)
Writing frames as png
To make movie execute:
ffmpeg -framerate 5 -pattern_type glob -i 'test_frame_*.png' -c:v libx264 -pix_fmt yuv420p out.mp4
 23.013333 seconds (48.09 M allocations: 2.580 GiB, 2.96% gc time, 1.27% compilation time)
Observables and @lift macros
 46.522218 seconds (29.64 M allocations: 3.416 GiB, 1.16% gc time, 1.94% compilation time)

```

---

<div class="post-metadata">

**Author:** ![\_bernhard](https://avatars.discourse-cdn.com/v4/letter/_/bc79bd/32.png) [@\_bernhard](https://discourse.julialang.org/u/_bernhard)\
**Post date:** [September 7, 2023, 7:21pm UTC](https://discourse.julialang.org/t/slow-makie-animations/103626/9 "2023-09-07T19:21:43Z")

</div>

I’m glad that this worked out for you. However, note that you can still use `Observable`s with `recordframe!`.

---

<div class="post-metadata">

**Author:** ![rkube](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rkube/32/211198_2.png) [@rkube](https://discourse.julialang.org/u/rkube)\
**Post date:** [September 7, 2023, 7:27pm UTC](https://discourse.julialang.org/t/slow-makie-animations/103626/10 "2023-09-07T19:27:00Z")

</div>

Thanks. I couldn’t find this in the documentation: [API · Makie](https://docs.makie.org/stable/api/index.html#record)  
Do you maybe have an example?
