Plotting and animating in Mathematica

Wolfram Mathematica is very adept at plotting in both 2D and 3D. So, the very first thing that we’re going to do is to learn some of its basic plotting functions.

  1. Basic plotting functions
  2. Plot options
    1. PlotStyle
    2. Filling and FillingStyle
  3. Combining plots
    1. Passing a list to the Plot[…] function
    2. GraphicsRow & GraphicsColumn
    3. Show[…]
  4. Animate and export

Basic plotting functions

One of the simplest lines of code that you can evaluate in WM is probably:

Plot[Sin[x], {x, 0, 2π}]
1

It consists of three main parts:

  1. The Plot function: Plot[Sin[x], {x, 0, 2π}]
  2. Any mathematical function of a single variable: Plot[Sin[x], {x, 0, 2π}]
  3. and the variable itself with its range of values: Plot[Sin[x], {x, 0, 2π}]

So, basically, if you have a function y[x] and you know the range of x, you’ll be able to plot the graph using the Plot function.

But, of course, we can plot things in many different ways.

For instance, y can sometimes not be expressed as a mathematical function of x. However, we can have both x and y expressed as functions of yet another parameter t (often called the evolution parameter): x[t], y[t].

In this case we’d use ParametricPlot:

ParametricPlot[{Cos[t], Sin[t]}, {t, 0, 2π}]
4

And you might know from basic mathematics that {Cos[t], Sin[t]} corresponds to a circle.

However, let’s say, we don’t have any mathematical functions, but we know all the points (meaning the {x, y} tuples) of our graph. We can use ListPlot or ListLinePlot to get the image:

ListPlot[
  {{0.0, 0},
   {0.5, 0.479},
   {1.0, 0.841},
   {1.5, 0.997},
   {2.0, 0.909},
   {2.5, 0.598},
   {3.0, 0.141},
   {3.5, -0.350},
   {4.0, -0.756},
   {4.5, -0.977},
   {5.0, -0.958},
   {5.5, -0.705},
   {6.0, -0.279}}
]
2
ListLinePlot[
  {{0.0, 0},
   {0.5, 0.479},
   {1.0, 0.841},
   {1.5, 0.997},
   {2.0, 0.909},
   {2.5, 0.598},
   {3.0, 0.141},
   {3.5, -0.350},
   {4.0, -0.756},
   {4.5, -0.977},
   {5.0, -0.958},
   {5.5, -0.705},
   {6.0, -0.279}}
]
3

Note, that neither of these functions have parameter range anymore, because they simply don’t need any parameters to be constructed: we supply them with all the points.

You can see, though, that ListLinePlot image is far less smooth than the Plot image for obvious reasons: Plot had the whole function and could generate as many points as it needed, while ListLinePlot had only 13 points and it connected them (as name suggests) with straight lines.

There are, of course, ways of generating smoother plots with only a few points using interpolation, but that’s another story.

And then there are 3D Plots. The names are really easy to remember, as you need to add “3D” at the end of a 2D command:

Plot3D[Sin[2x] + Sin[y], {x, 0, 2π}, {y, 0, π}]
5

And you might have noticed that we have an additional parameter here. We have:

  1. The Plot3D function: Plot3D[Sin[2x] + Sin[y], {x, 0, 2π}, {y, 0, π}]
  2. A mathematical function of two parameters: Plot3D[Sin[2x] + Sin[y], {x, 0, 2π}, {y, 0, π}]
  3. and these two parameters along with their ranges: Plot3D[Sin[2x] + Sin[y], {x, 0, 2π}, {y, 0, π}]

Which is obvious from mathematical point of view, because a surface in 3D space (having by itself 2 dimensions) is parametrized by two independent variables.

(By the way, there is no special reason why I use sines and cosines all the time… They’re just simple and easy on the eyes.)

Of course, we can plot 1D curves in 3D space too, but we are going to need ParametricPlot3D for that:

ParametricPlot3D[{t Sin[t], t Cos[t], t}, {t, 2π, 10π}]
6

ParametricPlot3D, actually, works for both curves and surfaces (all you have to do is pass another parameter), while Plot3D is constructed for creating surfaces only.

We have ListPlot3D too, where you just throw points {x, y, z} in and it connects those points with triangles:

ListPlot3D[
  {{0, 0, 1},
   {0, 1, 8},
   {0, 2, 5},
   {0, 3, 9},
   {1, 0, 0},
   {1, 1, 7},
   {1, 2, 0},
   {1, 3, 1},
   {2, 0, 2},
   {2, 1, 2},
   {2, 2, 7},
   {2, 3, 10},
   {3, 0, 5},
   {3, 1, 9},
   {3, 2, 5},
   {3, 3, 9}}]
7

Not the smoothest thing there is, but we may need it somewhere.

There are also some more exotic plotting functions like PolarPlot, which creates some very nice curves with ease, if you know how to use it: you need to pass the distance from the center as a function of angle r[θ].

PolarPlot[10 + Sin[10*θ], {θ, 0, 2π}]
8

And there are many more like LogPlot, ContourPlot, DensityPlot, etc. We’ll come back to them in future tutorials.

To sum up:

There are a number of ways in WM to visualize your numerical or symbolic data. Depending on which type of information you have, you might need different methods. You should use:

  1. Plot ↴
    to generate a curve with a single-parameter function y[x];
  2. ParameterPlot ↴
    to generate a curve with two single-parameter functions {x[t], y[t]};
  3. ListPlot ↴ or ListLinePlot ↴
    to generate a curve with a list of particular points {x, y};
  4. PolarPlot ↴
    to generate a curve with the radius function r[θ];
  5. Plot3D ↴
    to generate a surface with a two-parameter function z[x, y];
  6. ParametricPlot3D ↴
    to generate a curve or a surface with three single-parameter or two-parameter functions {x[t], y[t], z[t]} or {x[u, v], y[u, v], z[u, v]};
  7. ListPlot3D ↴
    to generate a surface with a list of particular points {x, y, z};

etc.

It’s a good idea to have this scheme in mind, but don’t worry if you can’t remember all of the names in a single take. It’ll come in practice and we’re going to see all of these in action in future tutorials as well.

Plot options

In Mathematica every function has primary and additional arguments. For example:

Plot[Sin[t], {t, 0, 2π}, PlotStyle -> Red]
11

Here Sin[t] and {t, 0, 2π} are primary arguments, while PlotStyle->Red is an additional one.

You must always pass primary arguments to the function, however additional parameters are not always needed, WM will still do its job in his own default way (in this example the curve would have been blue). Additional parameters help users customize outputs and achieve desired results.

All the arguments are separated by commas. And you have to be careful about the order of primary arguments. For instance,

Plot[{t, 0, 2π}, Sin[t], PlotStyle -> Red]

this code would not work, because Plot automatically assumes what the first and the second arguments are. If a function has even three or more primary arguments, you must always pass them in order and then move to additional arguments. In contrast, you can pass additional parameters in any order (basically, because they have names and WM will still be able to tell them apart: here PlotStyle is a name of parameter and Red is its value).

Every function of WM has a list of additional parameters in its documentation article. You can just collapse the options menu and find them all with various examples.

12

We’ll explore a few of them in this section.


PlotStyle

The PlotStyle option enables users to apply desired styling to the curves. Styles can vary by:

  1. Color: Plot[Sin[t], {t, 0, 2π}, PlotStyle -> Red]
    13
  2. Opacity: Plot[Sin[t], {t, 0, 2π}, PlotStyle -> Opacity[0.5]]
    14
  3. Line width: Plot[Sin[t], {t, 0, 2π}, PlotStyle -> Thick]
    15
  4. Line continuity: Plot[Sin[t], {t, 0, 2π}, PlotStyle -> Dashed]
    16

Or everything at the same time. For instance, you can have a thick orange dotted line with an opacity of 0.5, but there is a certain way to do this.

All these keywords (Red, Thick, Opacity[…], Dashed) are called graphical directives. And to use several of them at the same time, you need to wrap them up in Directive[…] function:

Directive[Orange, Thick, Dotted, Opacity[0.5]]

and then pass them to Plot as a parameter value:

Plot[Sin[t], {t, 0, 2π},
  PlotStyle -> Directive[Orange, Thick, Dotted, Opacity[0.5]]]
17

And here you might think that you’re still constrained by predefined styles of WM, but you’re really not.

Colors

Yes, there are predefined colors like Red, Green, Blue, Orange, etc. But, in fact, WM lets you use any color you want.

Firstly, there are commands like Lighter[…] and Darker[…], which obviously lighten and darken colors:

{Lighter[Orange], Orange, Darker[Orange]}
18

And if that’s not enough, you can always use RGBColor[…] command. Here’s how it works: you pass three primary values for red, green and blue (specifically, in this order) and it constructs a color out of these values. For instance,

RGBColor[0.2, 0.4, 0.6]
19

This ended up being a blueish color because the blue value is the highest.

And what’s even more advanced, you can include the alpha value (=opacity) too, if you add the fourth argument:

RGBColor[0.2, 0.4, 0.6, 0.5]

20 (which displays itself in a weird kind of way when evaluated, but it’ll look normal in action)

If you don’t know how to mix colors, there are a lot of helpers on the web. Google even has its own RGB color picker:

21

However, you will have to divide the values by 255 here, because most color pickers assign integer values from 0 to 255 to RGB, but WM assigns real numbers from 0 to 1.

Thickness

Thick and Thin commands in WM are actually different values of the same function.

You can check this by evaluating

{Thick, Thin}
22

And Thickness[…] function works with numbers, too. All you have to do is pass linewidth:

GraphicsRow[
 {Plot[Sin[t], {t, 0, 2π}, PlotStyle -> Thickness[0.001]],
  Plot[Sin[t], {t, 0, 2π}, PlotStyle -> Thickness[0.005]],
  Plot[Sin[t], {t, 0, 2π}, PlotStyle -> Thickness[0.01]],
  Plot[Sin[t], {t, 0, 2π}, PlotStyle -> Thickness[0.02]]}]
23

(don’t worry about GraphicsRow here, we’ll take a closer look at it in the next section)

Remark: Numbers here correspond neither pixels nor millimeters (or anything). Curve width scale corresponds to the scale of plot itself. For instance, if 0.02 is thick on this plot range, it’d look thinner on greater scales (and even thicker for smaller scales, for that matter). Just keep in mind to consider plot range before assigning curve thickness.

Dashing

And the same goes for dashing. Dashed and Dotted may seem like the only choices here, but they’re in fact defined for simplicity (by someone from behind the curtain) using the same function:

{Dashed, Dotted}
24

The Dashing[…] function works with a list of numbers and turns them into 0/1 values. For instance,

Dashing[{0.08, 0.02, 0.001, 0.02}]

would tell WM’s plotting device to

  1. Draw 0.08 units with maximum opacity;
  2. Draw the next 0.02 units with 0 opacity;
  3. Draw the next 0.001 units with maximum opacity;
  4. Draw the next 0.02 units with 0 opacity;
    . . . and so on, consecutively.
25

And this, of course, works for any sequence of numbers.

So, basically, WM allows you to plot with curves of any color, any opacity, any thickness and any dashing style. You just need to know, how to represent those in numbers.

Plot[Sin[t], {t, 0, 2π},
 PlotStyle -> Directive[
   Darker[Red],
   Opacity[0.7],
   Thickness[0.01],
   Dashing[{0.05, 0.02, 0.001, 0.02}],
   CapForm["Round"]
  ]
]
26

Filling and FillingStyle

One other thing that you can do with WM is fill space below, above or even in-between (we’ll learn this when we’ll start combining graphs) plots. For that you’re going to need the Filling parameter.

Filling values can be predefined keyboards like Bottom, Top or Axis

GraphicsRow[{
  Plot[Sin[t], {t, 0, 2π}, Filling -> Bottom],
  Plot[Sin[t], {t, 0, 2π}, Filling -> Axis],
  Plot[Sin[t], {t, 0, 2π}, Filling -> Top]
}]
27

Or you may also fill to a certain constant function. For that, you need to plug a number (y value) and you’re set:

GraphicsRow[{
 Plot[Sin[t], {t, 0, 2π}, Filling -> -0.5],
 Plot[Sin[t], {t, 0, 2π}, Filling -> 0.5],
 Plot[Sin[t], {t, 0, 2π}, Filling -> π/4]
}]
28

The convenient thing is, if you change the PlotStyle of the curve, filling color will automatically change accordingly:

Plot[Sin[t], {t, 0, 2π},
  PlotStyle -> Darker[Green], 
  Filling -> Axis
]
29

If you don’t want this, however, WM still allows you to use fill with any color using the FillingStyle parameter:

Plot[Sin[t], {t, 0, 2π},
 PlotStyle -> Darker[Green], 
 Filling -> Axis,
 FillingStyle -> Lighter[Yellow]
]
30

Alternatively, you can do something even more fancy and apply ColorFunction to your filling:

Plot[Sin[t], {t, 0, 2π},
 PlotStyle -> Darker[Green], 
 Filling -> Axis,
 ColorFunction -> "Rainbow"
]
31

What this does is applies a color map to function values. In this case (using “Rainbow”), the higher the value, the closer it is to red and vice versa for purple.

To demonstrate this a bit better, I’ll substitute my sine with another function for a while:

Plot[Sin[4t]+t, {t, 0, 2π},
 PlotStyle -> Darker[Green], 
 Filling -> Axis,
 ColorFunction -> "Rainbow"
]
32

Here the lowest value is 0 and the highest value is . And you can see how the color changes as values do.

There are many color maps. You can either browse the documentation for that or use WM built-in Color Schemes palette:

33

Or, of course, you can use any gradient you want using the Blend function:

Plot[Sin[t], {t, 0, 2π},
 PlotStyle -> Darker[Green], 
 Filling -> Axis,
 ColorFunction -> (Blend[{Lighter[Yellow], Darker[Green]}, #2] &)
]
34

Now, this is somewhat complicated, so let’s break it down a bit.

Blend[…] function (guess what?) blends colors. You pass two or more colors with respective ratios and it combines them like you would on a physical palette. For instance:

Blend[{Green, Blue}, 0.5]
35

The above line of code generates color between green and blue. Number 0.5 there means that the color is exactly in the middle of green and blue. The more closer the number is to 0, the closer the color is to green (and the same for 1 and blue):

{Blend[{Green, Blue}, 0],
 Blend[{Green, Blue}, 0.25],
 Blend[{Green, Blue}, 0.50],
 Blend[{Green, Blue}, 0.75],
 Blend[{Green, Blue}, 1]}
36

So, the way this works is, we’re using our graph info in the place of this number. #2 in the code tells WM to plug the second dimension (which is y) into the blend function. If we’d written #1, it would use the first dimension (being x) instead:

Plot[Sin[t], {t, 0, 2π},
 PlotStyle -> Darker[Green], 
 Filling -> Axis,
 ColorFunction -> (Blend[{Lighter[Yellow], Darker[Green]}, #1] &)
]
37

In 3D you can use #1, #2 and #3 (meaning respectfully x, y and z).

And if you’re still curious about other details (the & keyword, for instance), I will write a bit more about that here.


There are about a hundred more parameters (depending on when you’re reading this, there may be even much more) for 2D and 3D plots, but instead of cramming them here all together, we’re going to learn them along the way in future tutorials.

Combining plots

Passing a list to the Plot[…] function

Let’s take a look at the simplest and the most straightforward way.

Plot[{Sin[x], Sin[x - 0.2]}, {x, 0, 2π}]
40

Here, you can notice, we’ve passed a list of functions instead of a single function. And we’re not constrained to two-element lists, either. We can go as crazy as we want:

Plot[
 {Sin[x],
  Sin[x - 0.2],
  Sin[x - 0.4],
  Sin[x - 0.6],
  Sin[x - 0.8],
  Sin[x - 1.0],
  Sin[x - 1.2],
  Sin[x - 1.4],
  Sin[x - 1.6],
  Sin[x - 1.8],
  Sin[x - 2.0],
  Sin[x - 2.2],
  Sin[x - 2.4],
  Sin[x - 2.6],
  Sin[x - 2.8],
  Sin[x - 3.0],
  Sin[x - 3.2],
  Sin[x - 3.4],
  Sin[x - 3.6]},
 {x, 0, 2π}]
41

Now, while this is beautiful, any programmer would tell you that this code is a piece of garbage. We’re using the same function and we’re writing it manually nineteen times. Of course, if it’s a one-time thing (meaning, you’ll evaluate once, save the picture and forget about it forever), you’re free to write manually as much as you want, but that rarely is the case. Normally, you would want to go back and change some things to whatever you’ve been working on. Imagine, what we’d have to do if we decided that we want to plot functions with 0.3 intervals instead of 0.2. We’d have to change every single function manually. Nineteen times again. And, human error aside, that’s a lot of unnecessary work.

Fortunately, there are often neat ways (especially, in the world of programming) to do nineteen jobs at a single take. WM has a Table[…] function just for that.

Table[Sin[x - 0.2*i], {i, 0, 18}]
{Sin[x], Sin[x - 0.2], Sin[x - 0.4],
 Sin[x - 0.6], Sin[x - 0.8], Sin[x - 1.0],
 Sin[x - 1.2], Sin[x - 1.4], Sin[x - 1.6],
 Sin[x - 1.8], Sin[x - 2.0], Sin[x - 2.2],
 Sin[x - 2.4], Sin[x - 2.6], Sin[x - 2.8], 
 Sin[x - 3.0], Sin[x - 3.2], Sin[x - 3.4],
 Sin[x - 3.6]}

To break this down, we write Sin[x-0.2*i] as a function which depends on some iteration variable i and then let i run from 0 to 18. And voilà, we end up with that same nineteen-function list that we previously generated manually.

Now, if we pass that table into our plotting function, we end up with something interesting:

Plot[Table[Sin[x - 0.2*i], {i, 0, 18}], {x, 0, 2π}]
42

Well, that worked partly. We got our plots, but we’ve lost that beautiful spectrum of colors from before.

The reason is, previously we were passing a list of items to the plotting function, now we’re passing only one. Yeah, exactly: table is a single object. This might be confusing, because we’ve seen that Table[…] generates that same list written manually before. But let’s think about what’s the difference between these two lines:

Table[Sin[x - 0.2*i], {i, 0, 18}]
{Sin[x], Sin[x - 0.2], Sin[x - 0.4],
 Sin[x - 0.6], Sin[x - 0.8], Sin[x - 1.0],
 Sin[x - 1.2], Sin[x - 1.4], Sin[x - 1.6],
 Sin[x - 1.8], Sin[x - 2.0], Sin[x - 2.2],
 Sin[x - 2.4], Sin[x - 2.6], Sin[x - 2.8], 
 Sin[x - 3.0], Sin[x - 3.2], Sin[x - 3.4],
 Sin[x - 3.6]}

The difference is evaluation. The Table[…] (the input) turns into the list (the output) only after evaluation. Before, it’s only a single object (a single table) and Plot[…] treats it as a single object too: gives the same color to all its points.

The trick avoiding this is actually evaluating the table before passing it as an argument:

Plot[Evaluate[Table[Sin[x - 0.2*i], {i, 0, 18}]], {x, 0, 2π}]
43

And, as a side note, evaluation (as well as any other function) can be applied in a prettier way from the right side.

Evaluate[Table[Sin[x - 0.2*i], {i, 0, 18}]]
Table[Sin[x - 0.2*i], {i, 0, 18}] // Evaluate

The two lines above are equivalent. // is just the keyword that we use for applying functions from the right side.

So… the next time you see a random //Evaluate after the table, you’ll know why it’s there.

The colors we see here are automatic. But, we’re of course free to use whatever sequence we want. And you don’t need to worry about passing the same amount of colors as the curves, too. For instance:

Plot[Table[Sin[x - 0.2*i], {i, 0, 18}] // Evaluate, {x, 0, 2π}, 
 PlotStyle -> {Red, Green, Yellow}]
44

Here we passed only three colors to 19 curves, but as you can clearly see from the output, the colors keep repeating without any errors in the code. So, there is that.


GraphicsRow & GraphicsColumn

Sometimes, instead of placing plots onto one-another, we want to view them side-by-side. GraphicsRow[…] and GraphicsColumn[…], as names suggest, are used to align plots in a row or in a column.

GraphicsRow[{
  Plot[Sin[x], {x, 0, 2π}],
  Plot[x^2, {x, -1, 1}]
  }]
45

This is extremely helpful when the domains/ranges of two functions differ and we want to plot them with different axis scales.

GraphicsColumn[…] works in the exact same way.

GraphicsColumn[{
  Plot[Sin[x], {x, 0, 2 \[Pi]}],
  Plot[x^2, {x, -1, 1}]
  }]
46

And, if you have extra memory and spare variable names, you can clean up the input by storing plots in variables:

p1 = Plot[Sin[x], {x, 0, 2π}]
p2 = Plot[x^2, {x, -1, 1}]

The advantage (apart from a cleaner code) is that now you can use the same plot over and over again… for whatever reasons:

GraphicsColumn[{p1, p2, p1, p2}]
47

And, finally, the combination of rows and columns is a grid. To align plots on a grid, we use GraphicsGrid[…]:

GraphicsGrid[{{p1, p2}, {p2, p1}}]
48

Show[…]

One of the most important and powerful methods that we’ll see (and which I’m going to use it quite often in future parts of this course) is the Show[…] method.

It combines all kinds of graphics objects on a single image.

Say, you want to have two different plots onto each-other:

Show[p1, p2]
49

And it combines them on a single axis. Here you can see we have the whole p1 (from before) and a part of p2 onto it.

Why is p2 cut off, though? Because, when Show[…] builds an image, it has to define the plot range somehow. So it chooses the first graphics object, which, in our case, is p1. It would’ve been different if we passed the plots in an opposite order:

Show[p2, p1]
50

Here you can see that we see the whole p2 and only a part of p1 (whichever part fitted into the frame).

Also, you can notice that Show[…] doesn’t assign different colors to different plots in an automatic manner. That’s something you will have to do manually.

Show[
 Plot[Sin[x], {x, 0, 2π}, PlotStyle -> Red],
 Plot[x^2, {x, -1, 1}, PlotStyle -> Gray]
 ]
51

However, in principle, we would’ve done all these with Plot[…] function too. What we would not have done with it, is this:

Show[
 Plot[Sin[x], {x, 0, 2π}, PlotStyle -> Red, 
  AspectRatio -> Automatic],
 Plot[x^2, {x, -1, 1}, PlotStyle -> Gray],
 Graphics[Disk[{2, 0}, 0.2]],
 Graphics[Disk[{4, 0}, 0.2]],
 Graphics[Rectangle[{2.5, -0.5}, {3.5, -0.75}]],
 Graphics[Triangle[{{2.5, 0.5}, {3, 1}, {3.5, 0.5}}]]
 ]
52

As mentioned above, Show[…] accepts all graphics objects and places them on a single image. And there are a lot of graphics objects: lines, circles, disks, rectangles, triangles, polygons, etc. All wrapped inside a Graphics[…] function.

Here I used AspectRatio parameter to set equal scale to the x and y axes. You can ignore this now, we’ll probably come across with it again, anyway.

And, of course, you can apply different styles to different objects, as well.

Show[
 Plot[Sin[x], {x, 0, 2π}, PlotStyle -> Red, 
  AspectRatio -> Automatic],
 Plot[x^2, {x, -1, 1}, PlotStyle -> Gray],
 Graphics[{Directive[Opacity[0.7], Red], Disk[{2, 0}, 0.2]}],
 Graphics[{Directive[Opacity[0.7], Red], Disk[{4, 0}, 0.2]}],
 Graphics[{Directive[Opacity[0.3], Orange], 
   Rectangle[{2.5, -0.5}, {3.5, -0.75}]}],
 Graphics[{Directive[Opacity[0.5], Green], 
   Triangle[{{2.5, 0.5}, {3, 1}, {3.5, 0.5}}]}]
 ]
53

Which is all nice and custom.

But it’s a bit hard on the eyes. So, as a final note, let’s tidy things up a little bit by giving names to our figures:

eye1 = Graphics[{Directive[Opacity[0.7], Red], Disk[{2, 0}, 0.2]}]
eye2 = Graphics[{Directive[Opacity[0.7], Red], Disk[{4, 0}, 0.2]}]
mouth = Graphics[{Directive[Opacity[0.3], Orange], 
   Rectangle[{2.5, -0.5}, {3.5, -0.75}]}]
hat = Graphics[{Directive[Opacity[0.5], Green], 
   Triangle[{{2.5, 0.5}, {3, 1}, {3.5, 0.5}}]}]

And then using them in Show[…].

Show[p1, p2, eye1, eye2, mouth, hat]
53

Which, of course, gives the same image, but now you know what you’re plotting simply by taking a glance at the code.


This is most of the basic functions that are used in WM for combining graphics. Combined 3D plots and figures are generated in pretty much the same way.

For the sake of completeness, I should probably mention that there is a function called Overlay[…], which works in quite a similar way as Show[…] does. The difference is, all the graphics objects in Show[…] share the same axes/frame, when the graphics objects in Overlay[…] each have their own. Using Overlay[…] can be beneficial in a number of cases, but one has to be careful with aspect ratio, padding, etc. when overlaying plots with different axes/frames.

Animate and export

In principle, WM can animate anything, if you tell it precisely what to do. To see this, we first need to get to know with the function Animate[…] as simply as it gets:

Animate[n, {n, 0, 10}]
54
55

Now, I must admit, this is a pretty silly example, but it draws a picture (heh…).

Animate[…] function will (as the name suggests) animate anything that’s transferred as the first parameter. This, however, must be variable (otherwise, there’s nothing to be animated, really) and its variation is transferred as the second parameter.

Here, the first parameter is only a number n, which we later (taking a look at the second parameter) find out that varies from 0 to 10. So, here we go.

55

If you pass a bit more complicated input, say, a plot, as the first argument, you’ll end up with an animated plot:

Animate[Plot[Sin[n x], {x, 0, 2π}], {n, 1, 10}]
56

Here we have used the number n as a kind of frequency of plotted wave, so as n grows, we end up with more and more tight waves.

This is, basically, how Animate[…] works: you pass an object dependent on some dynamic variable as the first argument and specify the range of that variable in the second.

There are, of course, nuances and details, such as fixing the range, overlaying plots, exporting the animation, etc. and we’ll get down to that right now.

First of all, even before we hit evaluate, we want to make sure that we’re plugging the right parameters in the Animate[…] function. Consider the following example:

Say, we want to draw a sine wave (again, nothing too fancy) but we want our animation to draw it gradually, as one does with a pen. In other words, we want to draw the wave with a variable endpoint.

The straightforward way to do this is:

Animate[Plot[Sin[x], {x, 0, endpoint}], {endpoint, 0.1, 2π}]
57

Not exactly what we hoped for, is it? The code seemed okay, though: we’ve allowed the endpoint to vary. Instead of fixed , the plotting variable runs from 0 to some number between 0.1 and . So, what’s the matter?

Looking at the animation closely, this may already be obvious to you but let’s still take a single frame manually, say, when the endpoint is equal to 4.

Plot[Sin[x], {x, 0, 4}]
58

It’s clear now. The Plot[…] function cut the axis at x=4, because that’s where the plot ends. And each of these plots, being independent from each-other, don’t know our ultimate goal that the plot must be stretched all the way to x=2π. If we want it to know, we need to specify that by ourselves:

Animate[Plot[Sin[x], {x, 0, endpoint}, 
PlotRange -> {{0, 2π}, {-1, 1}}], {endpoint, 0.1, 2π}]
59.gif

And it works! All we’ve changed here was that now each of the plots has a fixed range 0<x<2π and -1<y<1. This kind of range fixing is often quite important when dealing with animations. Unless you’re 100% sure that the default range doesn’t change (or, unless you do want it to change), I wouldn’t let the Plot[…] function deal with that automatically. Because, even a slight change in the range makes the axes jump all around during the animation.

(By the way, starting at 0.1 has a purpose here: plotting something from 0 to 0 is something that WM doesn’t understand, so the first frame must be some small number above zero).

Now, let’s go one step higher with our little animation. Say, you want to put a red dot at the endpoint, while it draws the wave. This means, in a bit mathematical terms, that you want to have a filled circle (called disk in WM) with its center at the endpoint.

How’d you do it?

Well, the endpoint is always at the coordinates: x=endpointy=Sin[endpoint].

Let’s try this for a single fixed endpoint again:

Show[
Plot[Sin[x], {x, 0, 4}, PlotRange -> {{0, 2π}, {-1, 1}}],
Graphics[{Red, Disk[{4, Sin[4]}, 0.05]}]
]
60

Here we have used the Show[…] function to combine different graphics objects (more about that in the previous section). To animate this, we only need to plug it into the Animate[…] function and swap those manually written 4s everywhere with the endpoint variable.

Animate[
Show[
Plot[Sin[x], {x, 0, endpoint},
PlotRange -> {{0, 2π}, {-1, 1}}],
Graphics[{Red, Disk[{endpoint, Sin[endpoint]}, 0.05]}]
]
, {endpoint, 0.1, 2π}];
61.gif

It works just fine, but it’s not that pretty from a standpoint of any perfectionist. There might be at least two reasons for that:

  1. The circle is not really a circle, but an ellipse, for some reason;
  2. It gets violently cut off at the top and at the bottom of the plot.

Both these problems are, of course, easily manageable.

At first, the circle displays as an ellipse because of the aspect ratio. If you take a closer look on the scales of the plot, the circle is 0.1 in diameter both vertically and horizontally. The thing is, these 0.1 are different for the two axes. x axis is a little bit (almost twice, actually) more ‘squashed’ than the y axis. This can be easily controlled by setting

AspectRatio -> Automatic

in the Plot[…] function.

The second problem is even easier to solve: we just expand our vertical range a little bit, say, -1.2 to 1.2 instead of ones, so that the circles don’t get cut off:

Animate[
Show[
Plot[Sin[x], {x, 0, endpoint},
PlotRange -> {{0, 2π}, {-1.2, 1.2}},
AspectRatio -> Automatic],
Graphics[{Red, Disk[{endpoint, Sin[endpoint]}, 0.05]}]
]
, {endpoint, 0.1, 2π}];
62

And this, I think, is far better.

Anyways, WM allows you to change the style of the graphics objects in any way that’s pleasant to your eyes and then animate them.

Speaking of pleasant-for-eyes, you might not really like all those 5-6 lines of code crammed inside the Animate[…] function. We can easily rewrite this by defining the internal expression as a function called “frame”:

frame[t_] := Show[
Plot[Sin[x], {x, 0, t}, PlotRange -> {{0, 2π}, {-1.2, 1.2}},
AspectRatio -> Automatic],
Graphics[{Red, Disk[{t, Sin[t]}, 0.05]}]
]

t here is a conventional name for a parameter that is used for time evolution. And to animate this, we just write:

Animate[frame[t], {t, 0.1, 2π}]
62

This kind of decomposition is often a good idea when writing a long code. And there are many reasons for this. One of the main perks is that the code becomes human-readable: each time you define a frame function and then tell WM to animate these frames for different values of t.

Secondly, if you want to check out a specific value of t, you’re doing that easily:

frame[4]
63

In contrast to what we did previously, when we copied the code and plugged these 4s in several places manually. As any programmer would tell you, that was a terrible way to code.

Moreover, we may want to use this frame function in several places of the project, say, pick out some frames and arrange them in a row:

GraphicsRow[{frame[2], frame[4]}]
64

Or something… There are a lot of times and places here, when simply naming things makes our lives easier.


All right, since we’ve got a little bit of control already, it’s time to save our animations as a GIF (or whatever).

This is all done by the Export[…] function. But, before that, we need to store our frames. Animate[…] doesn’t store the animation anywhere, it just displays it. And for storing, we’re going to use Table[…]. In fact, this is very handy, because Animate[…] and Table[…] have the same syntax.

We’ve already seen

Animate[frame[t], {t, 0.1, 2π}]

Now we need

frames = Table[frame[t], {t, 0.1, 2π}]
65

And finally, to export:

Export["filename.gif", frames]

This saves your animation to a default working folder (we’ll learn how to change that in a minute) and if you take a look at this example, it looks like this:

66

Looks a bit fast and discrete, right? That’s easy to diagnose, when you look at the output of the Table[…] above: it only has seven frames.

As to why, unlike Animation[…] function, by default Table[…] changes variables by integer steps. In our example, t goes from 0.1 to  (which is approximately 6.28). Each time, t grows by 1, so all its values are: 0.11.12.13.14.15.16.1. These are the seven frames.

If we want to improve things, we can change the table step from 1 to, say, 0.1. This will make the animation ten times smoother, but will take ten times longer to compute (so, keep this in mind: you can’t just increase the quality without compromising evaluation time, that’s the way it always is).

frames = Table[frame[t], {t, 0.1, 2π, 0.1}]
Export["filename.gif", frames]
62

This will, again, save your GIF in the default working directory. To change the directory, one can use the SetDirectory[…] function. The way I usually do this, I set the working directory wherever the current WM file is located. And it’s done like this:

SetDirectory[NotebookDirectory[]]

After evaluating this line, all your exports will end up in the same directory as your working file.