Animating Lissajous curves with Python and matplotlib’s animation library

I’d like to plot an animation of Lissajous curves using Python and matplotlib’s animate library. I really do not have a lot of experience with Python, so rather than performance increasements, I’m looking for best practices to improve (and/or shorten) my code.

The following code produces a .gif file when evaluated as a jupyter-lab cell:

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.animation import PillowWriter

x_data = ()
y_data = ()
max_range = 1.2

f1 = 3        # sets the frequency for the horizontal motion
f2 = 5        # sets the frequency for the vertical motion
d1 = 0.0      # sets the phase shift for the horizontal motion
d2 = 0.5      # sets the phase shift for the vertical motion
delta1 = d1 * np.pi # I define the phase shift like this in order to use 
delta2 = d2 * np.pi # ...d1 and d2 in the export file name

fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(4,4), gridspec_kw={'width_ratios': (6, 1), 'height_ratios': (1, 6)})

for i in (ax1, ax2, ax3, ax4): 
    i.set_yticklabels(())
    i.set_xticklabels(())
    i.set_xticks(())
    i.set_yticks(())
    i.set_xlim(-max_range, max_range)
    i.set_ylim(-max_range, max_range)
ax2.set_visible(False)

line, = ax3.plot(0, 0)
line2 = ax3.scatter(0, 0)
linex = ax1.scatter(0, 0)
liney = ax4.scatter(0, 0)

def animation_frame(i):
    #print(i)
    ax1.clear()              # I tried to put this in a loop like this: 
    ax1.set_yticklabels(())  # for i in (ax1,ax3,ax4): 
    ax1.set_xticklabels(())  #     i.clear() 
    ax1.set_xticks(())       #     (... etc.)
    ax1.set_yticks(())       # but this didn't work
    ax3.clear()
    ax3.set_yticklabels(())
    ax3.set_xticklabels(())
    ax3.set_xticks(())
    ax3.set_yticks(())
    ax4.clear()
    ax4.set_yticklabels(())
    ax4.set_xticklabels(())
    ax4.set_xticks(())
    ax4.set_yticks(())
    ax1.set_xlim(-max_range, max_range)
    ax3.set_xlim(-max_range, max_range)
    ax3.set_ylim(-max_range, max_range)
    ax4.set_ylim(-max_range, max_range)
    x_data.append(np.sin(i * f1 + delta1))
    y_data.append(np.sin(i * f2 + delta2))
    x_inst = np.sin(i * f1 + delta1)
    y_inst = np.sin(i * f2 + delta2)
    line, = ax3.plot(x_data, y_data)
    line2 = ax3.scatter(x_inst, y_inst)
    linex = ax1.scatter(x_inst, 0)
    liney = ax4.scatter(0, y_inst)
    fig.canvas.draw()
    transFigure = fig.transFigure.inverted()       # in order to draw over 2 subplots
    coord1 = transFigure.transform(ax1.transData.transform((x_inst, 0)))
    coord2 = transFigure.transform(ax3.transData.transform((x_inst, y_inst)))
    my_line1 = matplotlib.lines.Line2D((coord1(0),coord2(0)),(coord1(1),coord2(1)), transform=fig.transFigure, linewidth=1, c='gray', alpha=0.5)
    coord1 = transFigure.transform(ax3.transData.transform((x_inst, y_inst)))
    coord2 = transFigure.transform(ax4.transData.transform((0, y_inst)))
    my_line2 = matplotlib.lines.Line2D((coord1(0),coord2(0)),(coord1(1),coord2(1)), transform=fig.transFigure, linewidth=1, c='gray', alpha=0.5)
    fig.lines = my_line1, my_line2,
    return line, line2, linex, liney

animation = FuncAnimation(fig, func=animation_frame, frames=np.linspace(0, 4*np.pi, num=800, endpoint=True), interval=1000)
animation.save('lissajous_{0}_{1}_{2:.2g}_{3:.2g}.gif'.format(f1,f2,d1,d2), writer='pillow', fps=50, dpi=200)
# This takes quite long, but since I'd like to have a smooth, slow animation, 
# ...I'm willing to accept a longer execution time.