Animation is a powerful tool to debug, test and demonstrate simulations.

It is possible to show a number of shapes (lines, rectangles, circles, etc), texts as well (images) in a window. These objects can be dynamically updated. Monitors may be animated by showing the current value against the time. Furthermore the components in a queue may be shown in a highly customizable way. As text animation may be dynamically updated, it is even possible to show the current state, (monitor) statistics, etc. in the animation windows.

Salabim’s animation engine also allows some user input.

It is important to realize that animation calls can be still given when animation is actually off. In that case, there is hardly any impact on the performance.

Salabim animations can be

  • synchronized with the simulation clock and run in real time (synchronized)
  • advanced per simulation event (non synchronized)

In synchronized mode, one time unit in the simulation can correspond to any period in real time, e.g.

  • 1 time unit in simulation time –> 1 second real time (speed = 1) (default)
  • 1 time unit in simulation time –> 4 seconds real time (speed = 0.25)
  • 4 time units in simulation time –> 1 second real time (speed = 4)

The most common way to start an animation is by calling `` env.animate(True)`` or with a call to animation_parameters(animate=True).

Animations can be started and stopped during execution (i.e. run). When main is active, the animation is always stopped.

The animation uses a coordinate system that -by default- is in screen pixels. The lower left corner is (0,0). But, the user can change both the coordinate of the lower left corner (translation) as well as set the x-coordinate of the lower right hand corner (scaling). Note that x- and y-scaling are always the same.
Furthermore, it is possible to specify the colour of the background with animation_parameters.

Prior to version 2.3.0 there was actually just one animation object class: Animate. This interface is described later as the new animation classes are easier to use and even offer some additional functionality.

New style animation classes can be used to put texts, rectangles, polygon, lines, series of points, circles or images on the screen. All types can be connected to an optional text.

Here is a sample program to show of all the new style animation classes:

# Animate

This program demonstrates the various animation classes available in salabim.
import salabim as sim

env = sim.Environment(trace=False)
env.modelname("Demo animation classes")

sim.AnimatePolygon(spec=(100, 100, 300, 100, 200, 190), text="This is\na polygon")
sim.AnimateLine(spec=(100, 200, 300, 300), text="This is a line")
sim.AnimateRectangle(spec=(100, 10, 300, 30), text="This is a rectangle")
sim.AnimateCircle(radius=60, x=100, y=400, text="This is a cicle")
sim.AnimateCircle(radius=60, radius1=30, x=300, y=400, text="This is an ellipse")
sim.AnimatePoints(spec=(100, 500, 150, 550, 180, 570, 250, 500, 300, 500), text="These are points")
sim.AnimateText(text="This is a one-line text", x=100, y=600)
Multi line text
Lorem ipsum dolor sit amet, consectetur
adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut
aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate
velit esse cillum dolore eu fugiat nulla

Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia
deserunt mollit anim id est laborum.

sim.AnimateImage("Pas un pipe.jpg", x=500, y=400)

Resulting in:


Animation of the components of a queue is accomplished with AnimateQueue(). It is possible to use the standard shape of components, which is a rectangle with the sequence number or define your own shape(s). The queue can be build up in west, east, north or south directions. It is possible to limit the number of component shown.

Monitors can be visualized dynamically with AnimateMonitor().

These features are demonstrated in Demo queue

import salabim as sim

This is a demonstration of several ways to show queues dynamically and the corresponding statistics
The model simply generates components that enter a queue and leave after a certain time.

Note that the actual model code (in the process description of X does not contain any reference
to the animation!

class X(sim.Component):
    def setup(self, i):
        self.i = i

    def animation_objects(self, id):
        the way the component is determined by the id, specified in AnimateQueue
        'text' means just the name
        any other value represents the colour
        if id == 'text':
            ao0 = sim.AnimateText(, textcolor='fg', text_anchor='nw')
            return 0, 16, ao0
            ao0 = sim.AnimateRectangle((-20, 0, 20, 20),
      , fillcolor=id, textcolor='white', arg=self)
            return 45, 0, ao0

    def process(self):
        while True:
            yield self.hold(sim.Uniform(0, 20)())
            yield self.hold(sim.Uniform(0, 20)())

env = sim.Environment(trace=False)

q = sim.Queue('queue')

qa0 = sim.AnimateQueue(q, x=100, y=50, title='queue, normal', direction='e', id='blue')
qa1 = sim.AnimateQueue(q, x=100, y=250, title='queue, maximum 6 components', direction='e', max_length=6, id='red')
qa2 = sim.AnimateQueue(q, x=100, y=150, title='queue, reversed', direction='e', reverse=True, id='green')
qa3 = sim.AnimateQueue(q, x=100, y=440, title='queue, text only', direction='s', id='text')

sim.AnimateMonitor(q.length, x=10, y=450, width=480, height=100, horizontal_scale=5, vertical_scale=5)

sim.AnimateMonitor(q.length_of_stay, x=10, y=570, width=480, height=100, horizontal_scale=5, vertical_scale=5)

sim.AnimateText(text=lambda: q.length.print_histogram(as_str=True), x=500, y=700,
    text_anchor='nw', font='narrow', fontsize=10)

sim.AnimateText(text=lambda: q.print_info(as_str=True), x=500, y=340,
    text_anchor='nw', font='narrow', fontsize=10)

[X(i=i) for i in range(15)]
env.modelname('Demo queue animation')

Here is snapshot of this powerful, dynamics (including the histogram!):



The various classes have a lot of parameters, like color, line width, font, etc.

These parameters can be given just as a scalar, like:

sim.AnimateText(text='Hello world', x=200, y=300, textcolor='red')

But each of these parameters may also be a:

  • function with zero arguments
  • function with one argument being the time t
  • function with two arguments being ‘arg’ and the time t
  • a method with instance ‘arg’ and the time t

The function or method is called at each animation frame update (maximum of 30 frames per second).

This makes it for instance possible to show dynamically the mean of monitor m, like in

sim.AnimateRectangle(spec=(10, 10, 200, 30), text=lambda: str(m.mean())

Class Animate

This class can be used to show:

  • line (if line0 is specified)
  • rectangle (if rectangle0 is specified)
  • polygon (if polygon0 is specified)
  • circle (if circle0 is specified)
  • text (if text is specified)
  • image (if image is specified)

Note that only one type is allowed per instance of Animate.

Nearly all attributes of an Animate object are interpolated between time t0 and t1. If t0 is not specified, now() is assumed. If t1 is not specified inf is assumed, which means that the attribute will be the ‘0’ attribute.


Animate(x0=100,y0=100,rectangle0==(-10,-10,10,10)) will show a square around (100,100) for ever
Animate(x0=100,y0=100,x1=200,y1=0,rectangle0=(-10,-10,10,10)) will still show the same square around (100,100) as t1 is not specified
Animate(,x0=100,y0=100,x1=200,y1=0,rectangle0=(-10,-10,10,10)) will show a square moving from (100,100) to (200,0) in 10 units of time.

It also possible to let the rectangle change shape over time:

Animate(,x0=100,y0=100,x1=200,y1=0,rectangle0=(-10,-10,10,10),rectangle1=(-20,-20,20,20)) will show a moving and growing rectangle.

By default, the animation object will not change anymore after t1, but will remain visible. Alternatively, if keep=False is specified, the object will disappear at time t1.

Also, colors, fontsizes, angles can be changed in a linear way over time.


Animate(,text='Test',textcolor0='red',textcolor1='blue',angle0=0,angle1=360) will show a rotating text changing from red to blue in 10 units of time.

The animation object can be updated with the update method. Here, once again, all the attributes can be specified to change over time. Note that the defaults for the ‘0’ values are the actual values at t=now().


an=Animate(t0=0,t1=10,x0=0,x1=100,y0=0,circle0=(10,),circle1=(20,)) will show a horizontally moving, growing circle.

Now, at time t=5, we issue an.update(t1=10,y1=50,circle1=(10,)) Then x0 will be set 50 (halfway 0 an 100) and cicle0 to (15,) (halfway 10 and 20). Thus the circle will shrink to its original size and move vertically from (50,0) to (50,50). This concept is very useful for moving objects whose position and orientation are controlled by the simulation.

Here we explain how an attribute changes during time. We use x as an example. Normally, x=x0 at t=t0 and x=x1 at t>=t1. between t=t0 and t=t1, x is linearly interpolated. An application can however override the x method. The prefered way is to subclass the Animate class:

# Demo animate 1
import salabim as sim

class AnimateMovingText(sim.Animate):
    def __init__(self):
        sim.Animate.__init__(self, text="", x0=100, x1=1000, y0=100, + 10)

    def x(self, t):
        return sim.interpolate(sim.interpolate(t, self.t0, self.t1, 0, 1) ** 2, 0, 1, self.x0, self.x1)

    def y(self, t):
        return int(t) * 50

    def text(self, t):
        return "{:0.1f}".format(t)

env = sim.Environment()


AnimateMovingText()  # otherwise the simulation will end at t=0, because there are no events left

This code will show the current simulation time moving from left to right, uniformly accelerated. And the text will be shown a bit higher up, every second. It is not necessary to use t0, t1, x0, x1, but is a convenient way of setting attributes.

The following methods may be overridden:

method circle image line polygon rectangle text

Dashboard animation

Here we present an example model where the simulation code is completely separated from the animation code. This makes communication and debugging and switching off animation much easier.

The example below generates 15 persons starting at time 0, 1, … . These persons enter a queue called q and stay there 15 time units.

The animation dashboard shows the first 10 persons in the queue q, along with the length of that q.

# Demo animate
import salabim as sim

class AnimateWaitSquare(sim.Animate):
    def __init__(self, i):
        self.i = i
            self, rectangle0=(-12, -10, 12, 10), x0=300 - 30 * i, y0=100, fillcolor0="red", linewidth0=0

    def visible(self, t):
        return q[self.i] is not None

class AnimateWaitText(sim.Animate):
    def __init__(self, i):
        self.i = i
        sim.Animate.__init__(self, text="", x0=300 - 30 * i, y0=100, textcolor0="white")

    def text(self, t):
        component_i = q[self.i]

        if component_i is None:
            return ""

def do_animation():
    for i in range(10):
    show_length = sim.Animate(text="", x0=330, y0=100, textcolor0="black", anchor="w")
    show_length.text = lambda t: "Length= " + str(len(q))

class Person(sim.Component):
    def process(self):
        yield self.hold(15)

env = sim.Environment(trace=True)

q = sim.Queue("q")
for i in range(15):
    Person(name="{:02d}".format(i), at=i)


All animation initialization is in do_animation, where first 10 rectangle and text Animate objects are created. These are classes that are inherited from sim.Animate.

The AnimateWaitSquare defines a red rectangle at a specific position in the sim.Animate.__init__() call. Note that normally these squares should be displayed. But, here we have overridden the visible method. If there is no i-th component in the q, the square will be made invisible. Otherwise, it is visible.

The AnimateWaitText is more or less defined in a similar way. It defines a text in white at a specific position. Only the text method is overridden and will return the name of the i-th component in the queue, if any. Otherwise the null string will be returned.

The length of the queue q could be defined also by subclassing sim.Animate, but here we just make a direct instance of Animate with the null string as the text to be displayed. And then we immediately override the text method with a lambda function. Note that in this case, self is not available!

Using colours

When a colour has to be specified in one of the animation methods, salabim offers a choice of specification:

  • #rrggbb rr, gg, bb in hex, alpha=255
  • #rrggbbaa rr, gg, bb, aa in hex, alpha=aa
  • (r, g, b) r, g, b in 0-255, alpha=255
  • (r, g, b, a) r, g, b in 0-255, alpha=a
  • “fg” current foreground color
  • “bg” current background color
  • colorname alpha=255
  • colorname, a alpha=a

The colornames are defined as follows:


This output can be generated with the following program:

# Show colornames

import salabim as sim

env = sim.Environment()
names = sorted(sim.colornames().keys())
env.modelname("show colornames")
x = 10
y = env.height() - 110
sx = 165
sy = 21

for name in names:
    sim.Animate(rectangle0=(x, y, x + sx, y + sy), fillcolor0=name)
        text=(name, "<null string>")[name == ""],
        x0=x + sx / 2,
        y0=y + sy / 2,
        textcolor0=("black", "white")[env.is_dark(name)],
    x += sx + 4
    if x + sx > 1024:
        y -= sy + 4
        x = 10

Running animations on PyDroid3

In order to run animations on PyDroid3 platforms, it required that the main program imports tkinter

import tkinter

Note that it can’t harm to include this import on non PyDroid3 platforms, apart from Pythonista, where tkinter is not available. In order to make a platform independent animation, you could use

if not sim.Pythonista:
    import tkinter


    import tkinter
except ImportError:

Avoiding crashes in tkinter

When animating a large number of objects, it is possible that tkinter crashes because there are too many tkinter bitmaps aka canvas objects, sometimes by issuing a ‘Fail to allocate bitmap’, sometimes without any message. Salabim limits the number of bitmap automatically by combining animation objects in one aggregated bitmap if the number of bitmaps exceeds a given maximum. Unfortunately it is not possible to detect this ‘Fail to allocate bitmap’, so it may take some experimentation to find a workable maximum (maybe going as low as 1000).

By default, salabim sets the maximum number of bitmaps to 4000, but may be changed with the Environment.maximum_number_of_bitmaps() method, or the maximum_number_of_bitmaps parameter of Environment.animation_parameters(). Choosing a too low maximum (particularly 0), may result in a performance degradation. The bitmap aggregation process is transparent to the user.

Note that does this not apply to the Pythonista implementation, where bitmaps are always aggregated.

Video production and snapshots

An animation can be recorded as an .mp4 (not under PyDroid3) or .avi video by specifying video=filename in the call to animation_parameters, or issue video(filename). The effect is that 30 times per second (scaled animation time) a frame is written. In this case, the animation does not run synchronized with the wall clock any more. Depending on the complexity of the animation, the simulation might run faster of slower than real time. In contrast to an ordinary animation, frames are never skipped.

The video has to be closed explicity with

or it is possible to use a context manager to automatically close a video file, like:


This will automatically close the file myvideo.mp4 upon leaving the with block.

It is also possible to create an animated gif or animated png files by specifying a .gif or .png file. In that case, repeat and pingpong are additional options. Note that animated gif/png are considerable bigger than ordinary video files. So, try and limit the length to 10 seconds. Animated pngs may be written with a transparent background (alpha < 255).

Video production supports also the creation of a series of individual frames, in .jpg, .gif, .png, .tiff or .bmp format. In this case, the video name has to contain an asterisk (*) which will be expanded at runtime to a 6 digit zero padded frame number, e.g.'test*.jpg')

will write individual autonumbered frames named


Prior to creating the frames, all files matching the specification will be removed, in order to get only the required frames, most likely for post processing with ffmpeg or similar.

Note that individual frame video production and animated gif/png production are available on all platforms, including Pythonista.

Salabim also supports taking a snapshot of an animated screen with Environment.snapshot().

Video creation on machines that do not support tkinter

On some servers, tkinter is not available. In that case it is still possible to create videos. That can be done by setting the blind_animation=True in the call to sim.Environment.

Note that this can also be used to (slightly) increase the performance of video production.

Audio support

On Windows platforms, it is possible to add an audio track to a video. With an audio track (usually an mp3 file) will be added. The audio may stopped by issueing audio(""). If another audio is started, the current audio, if any, will be stopped.

Adding audio to a video requires that ffmpeg is installed and in the search path. Refer to for downloads and instructions.

In order to develop lip synced videos, it is possible to play audios parallel to a simulation, provided the animation speed is equal to the audio_speed (1 by default). Audio playback is supported on Pythonista and Windows platforms only.