Engee documentation
Notebook

Animation of fireworks using Makie

firework_with_wind.gif

Makie is a popular visualization library in the julia ecosystem.
In this example, topics such as:

  • Using color palettes (colormaps/colorschemes)
  • Create mp4 animations (as an alternative @gif in Plots.jl)
  • features of building point-by-point graphs (scatter):
  • size
    • marker
    • color
    • alpha channel

The salute model as a system of differential equations

The salyut model in this example is described by a system of ordinary differential equations (ODES), which simulates the movement of particles under the influence of gravity, air resistance and horizontal wind. The system includes four variables: coordinates and , as well as their derivatives with respect to time (velocity and ). The equations have the form:

Here — acceleration of free fall, — air resistance coefficient, — the mass of the particle, — wind speed, — wind resistance coefficient. This system is solved numerically using the package DifferentialEquations.jl, which makes it possible to simulate the realistic behavior of fireworks particles after an explosion.


Installing dependencies and importing libraries

To work with animation and solving differential equations, we will need packages DifferentialEquations, CairoMakie and Random. We will install them, if they are not already installed, and import them.

In [ ]:
import Pkg; Pkg.add(["DifferentialEquations","CairoMakie","Random"])
using DifferentialEquations
using CairoMakie
using Random

# Устанавливаем seed для воспроизводимости
Random.seed!(1945_2025)

Program structure

Our program consists of several key components: a set of fireworks, each of which contains particles, a trajectory is calculated for each particle, and the trajectory is a set of points. .

fireworks_diagram.png

Defining the model and parameters

Defining the function particle_motion!, which describes the motion of particles according to the above system of ODES. We also set simulation parameters: acceleration of gravity, air resistance, particle mass, wind speed, and others.

In [ ]:
# Функция движения частицы с учетом ветра
function particle_motion!(du, u, p, t)
    g, k, m, v_w, k_w = p
    du[1] = u[3]  # dx/dt = v_x
    du[2] = u[4]  # dy/dt = v_y
    du[3] = -(k + k_w) / m * u[3] + k_w * v_w / m  # dv_x/dt = -(k + k_w)/m * v_x + k_w * v_w/m
    du[4] = -g - k / m * u[4]  # dv_y/dt = -g - k/m * v_y
end

# Параметры
g = 9.81      # ускорение свободного падения
k = 0.1       # коэффициент сопротивления воздуха
m = 0.1       # масса частицы
v_w = 0.1     # скорость горизонтального ветра (м/с, положительная — вправо)
k_w = 0.1     # коэффициент сопротивления ветру
n_fireworks = 3  # количество салютов
n_particles_per_firework = 150  # частиц на салют
v0_values = [1:2.5:25;]  # разные начальные скорости
tspan = (0.0, 1.5)  # общий временной интервал
saveat = 0.0125  # фиксированный шаг времени

Fireworks generation and color palette selection

Each salute is characterized by a random starting position ((x_0, y_0)), the time of the explosion, and a set of three color palettes. We use Makie.ColorSchemes to create a visually appealing effect, where the particles of each fireworks are painted in three different color schemes.

In [ ]:
# Список палитр
available_palettes = [
    Makie.ColorSchemes.Reds,
    Makie.ColorSchemes.pink,
    Makie.ColorSchemes.solar,
    Makie.ColorSchemes.Blues
]

# Определение салютов
fireworks = []
for i in 1:n_fireworks
    # Случайная начальная позиция
    x0 = rand(-12:12)
    y0 = rand(5:3:15)
    # Случайное время взрыва (между 0.0 и 75% tspan)
    explosion_time = rand(tspan[1]:0.1:0.75*tspan[2])
    # Выбор трёх случайных палитр для салюта
    selected_palettes = shuffle(available_palettes)[1:3]  # выбираем 3 разные палитры
    push!(fireworks, (x0=x0, y0=y0, explosion_time=explosion_time, palettes=selected_palettes))
end

Modeling of particle trajectories

For each salute, we simulate the trajectories of its particles by solving a system of ODES. Each particle is assigned one of three selected palettes to create a multicolored fireworks effect.

In [ ]:
# Моделирование траекторий для каждого салюта
all_trajectories = []
all_particle_palettes = []
for (idx, fw) in enumerate(fireworks)
    trajectories = []
    particle_palettes = []
    # Разделяем частицы на три группы для трёх палитр
    particles_per_palette = div(n_particles_per_firework, 3)
    for i in 1:n_particles_per_firework
        # Выбор начальной скорости из v0_values
        n_v = length(v0_values)
        v0 = v0_values[ceil(Int, (i - 1) / (n_particles_per_firework / n_v))%n_v+1] + rand(-2:0.25:2)
        θ = 2π * (i - 1) / (n_particles_per_firework / n_v) + rand(-0.1:0.001:0.1)  # случайное отклонение угла
        u0 = [fw.x0, fw.y0, v0 * cos(θ), v0 * sin(θ)]  # начальная позиция и скорость
        prob = ODEProblem(particle_motion!, u0, tspan, (g, k, m, v_w, k_w))
        sol = solve(prob, Tsit5(), saveat=saveat)
        push!(trajectories, [(u[1], u[2]) for u in sol.u])
        # Назначаем палитру в зависимости от группы частицы
        palette_idx = (i - 1) ÷ particles_per_palette + 1
        if palette_idx > 3  # для последнего набора частиц
            palette_idx = 3
        end
        push!(particle_palettes, fw.palettes[palette_idx])
    end
    push!(all_trajectories, trajectories)
    push!(all_particle_palettes, particle_palettes)
end

Creating a scene and adding a starry background

Before creating the animation, we set a scene with a black background and add stars that will be displayed throughout the animation. This creates a night sky effect.

In [ ]:
# Визуализация
fig = Figure(backgroundcolor=:black)
ax = Axis(fig[1, 1], aspect=1, limits=(-20, 20, -20, 20),
    backgroundcolor=:black, xgridvisible=false, ygridvisible=false,
    xticksvisible=false, yticksvisible=false,
    xticklabelsvisible=false, yticklabelsvisible=false)

# Добавление звёзд на задний фон
n_stars = 50  # Количество звёзд
x_stars = rand(-20:0.1:20, n_stars)  # Случайные x-координаты
y_stars = rand(-20:0.1:20, n_stars)  # Случайные y-координаты

Creating animations

Using the function record from CairoMakie to create MP4 animations. In each animation frame, we redraw the stars and fireworks particles that have already exploded, taking into account the tail effect and individual fading.

In [ ]:
# Анимация
total_frames = ceil((tspan[2] - tspan[1]) / saveat) + 1  # общее количество кадров
record(fig, "firework_with_wind.mp4", 1:total_frames-1) do frame_idx
    empty!(ax)
    # Перерисовываем звёзды
    CairoMakie.scatter!(ax, x_stars, y_stars, marker=:star5, color=:yellow, markersize=5, alpha=0.5)
    # Текущее время в анимации
    current_time = (frame_idx - 1) * saveat

    # Проходим по каждому салюту
    for firework_idx in 1:n_fireworks
        fw = fireworks[firework_idx]
        trajectories = all_trajectories[firework_idx]
        particle_palettes = all_particle_palettes[firework_idx]
        # Проверяем, взорвался ли салют
        time_since_explosion = current_time - fw.explosion_time
        if time_since_explosion >= 0
            # Вычисляем индекс времени относительно времени взрыва
            i = Int(round(time_since_explosion / saveat)) + 1
            # Ограничиваем i длиной траектории
            i = min(i, length(trajectories[1]))
            # Индивидуальное выцветание для каждого салюта
            if time_since_explosion >= 0
                fading_start = 0.6  # 60% от времени видимости
                fading_duration = 0.4  # оставшиеся 40% для выцветания
                k_fading = time_since_explosion < fading_start ? 1.0 : max(0.0, 1.0 - (time_since_explosion - fading_start) / fading_duration)
                # Ограничиваем длину хвоста: отображаем последние 24 позиции
                for (traj, palette) in zip(trajectories, particle_palettes)
                    for j in max(1, i - 24):i
                        alpha = k_fading * (0.1 + 0.9 * exp(-0.4 * (i - j)))
                        markersize = 2 + 3 * exp(-0.5 * (i - j))
                        t = i > 1 ? (j - max(1, i - 14)) / (i - max(1, i - 14)) : 1.0
                        color = palette[t]
                        CairoMakie.scatter!(ax, [traj[j]], markersize=markersize, color=color, alpha=alpha)
                    end
                end
            end
        end
    end
end

The animation may take a certain amount of time, after which the file will be created. "firework_with_wind.mp4"

Conclusion

In this example, we created a fireworks animation using the ecosystem Makie with a backend CairoMakie. We've learned how to simulate particle motion using differential equations, apply color palettes for visual effect, and create MP4 animations. This approach can be adapted for other visualization tasks, such as modeling planetary motion, simulating explosions, or creating artistic animations.