If you're seeing this message, it means we're having trouble loading external resources on our website.

If you're behind a web filter, please make sure that the domains *.kastatic.org and *.kasandbox.org are unblocked.

Main content

Oscillation amplitude and period

Are you amazed yet? In the angular motion section, we saw some pretty great uses of tangent (for finding the angle of a vector) and sine and cosine (for converting from polar to Cartesian coordinates). We could stop right here and be satisfied. But we’re not going to. This is only the beginning. What sine and cosine can do for you goes beyond mathematical formulas and right triangles.
Let’s take a look at a graph of the sine function, where y = sine(x)
You’ll notice that the output of the sine function is a smooth curve alternating between –1 and 1. This type of a behavior is known as oscillation, a periodic movement between two points. Plucking a guitar string, swinging a pendulum, bouncing on a pogo stick—these are all examples of oscillating motion.
And so we happily discover that we can simulate oscillation in a ProcessingJS program by assigning the output of the sine function to an object’s location. Note that this will follow the same methodology we applied to Perlin noise in the noise section.
Let’s begin with a really basic scenario. We want a circle to oscillate from the left side to the right side of our canvas.
This is what is known as simple harmonic motion (or, to be fancier, “the periodic sinusoidal oscillation of an object”). It’s going to be a simple program to write, but before we get into the code, let’s familiarize ourselves with some of the terminology of oscillation (and waves).
Simple harmonic motion can be expressed as any location (in our case, the x location) as a function of time, with the following two elements:
  • Amplitude: The distance from the center of motion to either extreme
  • Period: The amount of time it takes for one complete cycle of motion
Looking at the graph of sine embedded above, we can see that the amplitude is 1 and the period is TWO_PI; the output of sine never rises above 1 or below -1; and every TWO_PI radians (or 360 degrees) the wave pattern repeats.
Now, in the ProcessingJS world we live in, what is amplitude and what is period? Amplitude can be measured rather easily in pixels. In the case of a window 200 pixels wide, we would oscillate from the center 100 pixels to the right and 100 pixels to the left. Therefore:
// Our amplitude measured in pixels
var amplitude = 100;
Period is the amount of time it takes for one cycle, but what is time in our ProcessingJS world? I mean, certainly we could say we want the circle to oscillate every three seconds. And we could track the milliseconds elapsed in our program (using millis()) and come up with an elaborate algorithm for oscillating an object according to real-world time.
We have another option, however: we can use the fact that ProcessingJS programs have a notion of "frames", and that by default, a program attempts to run 30 "frames per second." ProcessingJS gives us the frameCount variable to find out what frame we're currently on, and the frameRate() function to change the preferred frames per second. 30 FPS is the default frame rate, as that's a good rate to produce a smooth animation to trick the human brain, but it can sometimes be helpful to slow down that frame rate, like when debugging.
We can thus decide to base our period on number of frames elapsed, as we've seen its closely related to real world time- we can say that the oscillating motion should repeat every 30 frames, or 50 frames, or 1000 frames, etc.
// Our period is measured in frames (our unit of time for animation)
var period = 120;
Once we have the amplitude and period, it’s time to write a formula to calculate x as a function of time, which we will substitute the current frame count for.
var x = amplitude * sin(TWO_PI * frameCount / period);
Let’s dissect the formula a bit more and try to understand each component. The first is probably the easiest. Whatever comes out of the sine function we multiply by amplitude. We know that sine will oscillate between -1 and 1. If we take that value and multiply it by amplitude then we’ll get the desired result: a value oscillating between -amplitude and amplitude. (Note: this is also a place where we could use ProcessingJS’s map() function to map the output of sine to a custom range.)
Now, let’s look at what is inside the sine function:
TWO_PI * frameCount / period
What’s going on here? Let’s start with what we know. We know that sine will repeat every 2*PI radians—i.e. it will start at 0 and repeat at 2*PI, 4*PI, 6*PI, etc. If the period is 120 frames, then we want the oscillating motion to repeat when the frameCount is at 120 frames, 240 frames, 360 frames, etc. frameCount is really the only variable; it starts at 0 and counts upward. Let’s take a look at what the formula yields with those values.
frameCountframeCount / periodTWO_PI * frameCount / period
000
600.5PI
1201TWO_PI
24022 * TWO_PI (or 4 * PI)
etc.  
frameCount divided by period tells us how many cycles we’ve completed—are we halfway through the first cycle? Have we completed two cycles? By multiplying that number by TWO_PI, we get the result we want, since TWO_PI is the number of radians required for one sine to complete one cycle.
Wrapping this all up, here’s the program that oscillates the x position of a circle with an amplitude of 100 pixels and a period of 120 frames.
It’s also worth mentioning the term frequency: the number of cycles per time unit. Frequency is equal to 1 divided by period. If the period is 120 frames, then only 1/120th of a cycle is completed in one frame, and so frequency = 1/120 cycles/frame. In the above example, we simply chose to define the rate of oscillation in terms of period and therefore did not need a variable for frequency.
Note that we worked through all of that using the sine function (sin() in ProcessingJS), but the same ideas apply to using the cosine function. The period is the same for both, and the main difference is just whether the beginning amplitude starts at 1 or at 0.

This "Natural Simulations" course is a derivative of "The Nature of Code" by Daniel Shiffman, used under a Creative Commons Attribution-NonCommercial 3.0 Unported License.

Want to join the conversation?

  • leafers sapling style avatar for user chewe  maxwell
    How does the map(y,-1,1,100,200) work?
    (2 votes)
    Default Khan Academy avatar avatar for user
    • spunky sam blue style avatar for user Dalendrion
      Imagine a line stretching from -1 to 1. There's a dot somewhere on that line, called "y".
      The map function stretches that line out like a rubber band so that it starts at 100 and ends at 200.
      That means the point "y" moved as well. That's the value the map function returns.
      (12 votes)
  • blobby green style avatar for user Szymon Wańczyk
    Does anybody know why my buttons does not work on browser? Here on Khan academy everything is fine but when I wanted to put my proccessing js code on my own website, interaction with keyboard buttons does not work. Interaction with mouse work well.
    (5 votes)
    Default Khan Academy avatar avatar for user
  • female robot grace style avatar for user ☯️ 𝕄𝕚𝕒 ℂ𝕙𝕒𝕪𝕞𝕒𝕟𝕘 ☯️
    I'm sort of stuck on Step 1. Can anyone help? I'm a little confused. PLEASE RESPOND.
    (3 votes)
    Default Khan Academy avatar avatar for user
    • old spice man green style avatar for user Bob Lyon
      The hint show three lines of code with three different colored boxes:
          var orange = sin(TWO_PI * frameCount / pink);
      var blue = map(...);
      drawSlinky(width/2, 10, blue);


      Working backwards, the blue box needs to be the Y coordinate that is the third parameter to drawSlinky. So line 2 simply declares a variable to hold that blue value. How? By mapping the the value of the orange box in line one.

      Since the value of the orange box is the results of the sin function, it is guaranteed to be between -1 and +1.

      The pink box in line one is a constant and a bizarre attempt to help you convert degrees to radians.
      (6 votes)
  • male robot hal style avatar for user yogesh kumar
    what does the overlap variable actually do in the next challenge?
    (3 votes)
    Default Khan Academy avatar avatar for user
    • starky tree style avatar for user Adrianna
      The overlap variable is not a special JS command like draw, it could be named anything! How it's value is used is what counts here. When it is used to multiply "space" in the y value of the ellipse function, it causes the y positions to be drawn at .8 their original value, which means a little higher up the screen than normal, or multiplying it by 1. It is also used to define space by dividing endY by overlap. This just makes the slinky a little longer. If you remove overlap here, the slinky will shrinky.
      The way the slinky actually bounces is by dynamically changing the width using the periodic sine function. As space is defined by passing in the endY argument, and the endY argument passed in is a sin function, the y position also dynamically changes with each period!
      (3 votes)
  • mr pants teal style avatar for user Reed Fagan
    Are their examples of oscillating motion correct? With the guitar pick ("plucking") and pogo stick examples it seems they are conflating oscillating motion - back and forth swinging around a point - with reciprocating motion - back and forth movement along a line.

    (I imagine a plucked guitar string might show something like oscillating motion but most of the motion would be reciprocating.)
    (3 votes)
    Default Khan Academy avatar avatar for user
  • hopper happy style avatar for user Andon Peine
    OK I think that I am officially confused, I am trying to do the next challenge "Rainbow Slinky" and I got it to work, but I can't move on. I keep getting an error saying "Use the sin() function to calculate the y position of the bottom of the slinky, and map() to convert it to a reasonable value." Is there something wrong with my code?

    angleMode = "radians";

    var period = 120;
    var amplitude = 100;

    var drawSlinky = function(centerX, startY, endY) {
    noFill();
    colorMode(HSB);
    strokeWeight(2);
    ellipseMode(CENTER);
    var overlap = 0.8;
    var space = (endY/overlap - startY)/30;
    for (var i = 0; i < 30; i++) {
    stroke(i*9, 200, 255);
    ellipse(centerX, i*space*overlap + startY, 60, space);
    }
    };

    draw = function() {
    background(255);

    var x = amplitude * sin(TWO_PI * frameCount / period);
    var y = map(x, -1, 1, 100, 105);
    drawSlinky(width/2, 10, y);
    };
    (2 votes)
    Default Khan Academy avatar avatar for user
  • blobby green style avatar for user Carol Tamez Melendez
    How can I calculate the maximum range of an oscillation? Most webpages talk about the calculation of the amplitude but I have not been able to find the steps on calculating the maximum range of a wave that is irregular.
    (2 votes)
    Default Khan Academy avatar avatar for user
  • blobby green style avatar for user Osomhe  Aleogho
    Please look out my code and tell me what is wrong with it and where. It's saying 'Think about the output of the sin() function, and what you pass as the start and end of the original range for map()'.
    CODE:
    angleMode = "radians";

    var drawSlinky = function(centerX, startY, endY) {
    noFill();
    colorMode(HSB);
    strokeWeight(2);
    ellipseMode(CENTER);
    var overlap = 0.8;
    var space = (endY/overlap - startY)/30;
    for (var i = 0; i < 30; i++) {
    stroke(i*9, 200, 255);
    ellipse(centerX, i*space*overlap + startY, 60, space);
    }
    };

    draw = function() {
    background(255);
    var a = sin(TWO_PI * frameCount /2 );
    var b = map(0, 0, 0, 0, 20);

    drawSlinky(width/2, 10, b);
    };

    Thanks.
    (1 vote)
    Default Khan Academy avatar avatar for user
  • leaf grey style avatar for user TheWatcherOfMoon
    I don't really understand the TWO_PI. Can someone explain a bit better?
    (1 vote)
    Default Khan Academy avatar avatar for user
  • piceratops ultimate style avatar for user ZeeWorld
    Why do they change the angle mode and translate the canvas?
    (1 vote)
    Default Khan Academy avatar avatar for user
    • old spice man green style avatar for user Bob Lyon
      As they state at the end of the tutorial, it is derived from sources outside of Khan Academy.

      The rest of the world (indeed, the universe, nature, your god(s)) use radians as angle measures. Only Khan Academy supports degrees and the ill-architected angleMode. So to readily port the outside code, they need to use radians, ...naturally...

      The translation moves the origin to the center of the canvas, something our Algebra teachers would approve of...
      (2 votes)