Display a simulation using Tkinter
As I didn't get any answer, I propose the following improvements.

First I didn't change the functions create_circle and coords_circle.

I found a more satisfying solution for the display function. Indeed, I create a class Display and implement the former display function in its __init__ method.

class Display:
    """Define the window used to display a simulation"""

    def __init__(self, balls, step, size):
        """Initialize and launch the display"""
        self.balls = balls
        self.step = step
        self.size = size

        self.window = tk.Tk()
        self.canvas = tk.Canvas(self.window, width=self.size, height=self.size, bg="black")
        self.canvas.pack()
        self.canvas.focus_set()
        self.drawing = self.create()
        self.started = False

        start_button = tk.Button(self.window, text="Start", command=self.start)
        stop_button = tk.Button(self.window, text="Pause", command=self.stop)
        start_button.pack()
        stop_button.pack()

        self.window.mainloop()


In this way, I can define the animate and stop functions as methods of the class Display rather than inside __init__.

The objects balls, step, size, window, canvas, drawing, start_button and stop_button become attributes of the class Display. So I don't need to put them in arguments of create and update methods.

def create(self):
    """Create a drawing item for each solver.Ball object

    return a dictionary with solver.Ball objects as keys and their circle drawings as items
    """
    return {ball: self.canvas.create_circle(ball.position[0], ball.position[1], ball.radius, fill="white") for ball in self.balls}

def update(self):
    """Update the drawing items for a time step"""
    solver.solve_step(self.balls, self.step, self.size)
    for ball in self.balls:
        self.canvas.coords_circle(self.drawing[ball], ball.position[0], ball.position[1], ball.radius)
    self.canvas.update()


Moreover, the start button calls the start method that calls the animate method only if the value of started is False. Thus, we don't have the bad behaviour raised in the question (need to push the pause button twice after pushing the start button twice).

def start(self):
    """Start the animation"""
    if not self.started:
        self.started = True
        self.animate()

def animate(self):
    """Animate the drawing items"""
    if self.started:
        self.update()
        self.window.after(0, self.animate)

def stop(self):
    """Stop the animation"""
    self.started = False


You'll find the complete code here.