Clicking on the screen results in a sound playing and a colourful circular wave emanating from the click location.
Example (requires Java plugin): http://gailcarmichael.com/processing/workshop/SoundWaves/
Concepts used: coordinates, colours, mouse interaction, objects, lists, shapes, sounds
Here’s a general overview of what we’re doing in this project:
Let’s begin our project by breaking the problem down a bit. Let’s say for now that we only want one circle on the screen at a time (if the user clicks again before the circle is done, we forget about the old circle and start a new one). We’ll also say that one circle will be the same colour no matter where it is on the screen.
There are a number of things we need to know about our circle. We have to keep track of where it is on the screen, how big it is at a given moment in time, and what colour it is. Because we have multiple bits of data to keep track of, it would be worth making a Circle class to store it all in one object.
(If you need to review how objects work, have a look at our slides from this morning, or read this more in-depth tutorial. Remember a class is like a blueprint, and the object is the model we create.)
In the new tab, create your new class that will define what a circle can be like:
class Circle
{
// we’ll put variables in here
}
We need to add a few variables to our class. Remember that variables we declare here will be associated with each individual object we create; that is, there will be a whole new set of these variables each time we make a new circle.
Here are the variables we need to add:
That should do it for now. Go back to the main tab (the one with the same name as your project) to get ready for the next step.
In this step, we’ll test out our Circle class and make it grow each frame (we’ll just do it at the beginning of the program for now, and make changes later so that circles are created when we click the mouse).
Let’s start adding some variables at the top of the main tab of our project. We need to keep track of what radius a circle should start with, how much the radius show grow by each frame, and how big the radius should grow before we’re done with it. We should put all this together at the top so we can use the numbers more than once, but only have to change them in one place if we don’t like them.
// Variables
int initCircleRadius = 5; // start size
int maxCircleRadius = 250; // max size
int deltaCircleRadius = 5; // how much to grow the radius each frame
We also need a variable to store a circle object, so go ahead and declare a new variable of type Circle in the same place.
Now that we have a variable to store our Circle in, we have to actually create a new object using the class definition we made earlier. We can do this in the setup() method, since we only need to do it once when the program starts.
You can create a new circle like this:
myCircle = new Circle();
Now that we’ve created a circle, we should probably fill in some details about its colour, etc. For example, we should set its radius to be the starting radius we defined earlier:
myCircle.radius = initCircleRadius;
To test that what we’ve done so far works, let’s draw our circle on the screen. We need to write a draw() method, and inside that, draw our circle. If you need to review how the draw() method works, check out the relevant Processing reference page.
Go ahead and create a draw() method, and inside it, start by making the background a solid white (it’s good to start each frame with a blank canvas).
We’ll draw an ellipse (since a circle is really just an ellipse with equal radii). Use your circle object’s variables to draw the appropriate ellipse (that is, don’t use arbitrary numbers, but get the values stored inside your object). I made my circle have a thicker-than-default stroke (i.e. outline) with the colour stored in my object, and no fill.
Next up, we want to make our circle get a bit bigger for each frame of the program’s animation so it looks like it’s growing.
The draw() method is already called each frame, so let’s take advantage of that. After displaying the circle to the screen, we can simply add to its radius so that it will appear bigger the next frame when it’s drawn again.
Run the program to see whether your circle grows.
Did it work? Good! But you may have noticed one little problem -- the circle just keeps on growing until it’s off the screen. We actually want it to stop before that. Use the maxCircleRadius you defined earlier to check the radius every time draw() is called. If the radius is bigger than the max, don’t draw the ellipse.
Now let’s change our program so that the circle appears only when the mouse is clicked, and is centered on the location of the click.
To do this, we need a mouseClicked() method. Check out the reference page.
In mouseClicked(), we want to create a new circle. This will replace the circle creation we did in setup(), since we don’t want a circle at the beginning of the program anymore.
Once that’s working, you can change the location of the circle to be where the mouse was clicked. The cursor location at any given time is stored in Processing’s special predefined variables mouseX and mouseY.
In the example, you may notice that the circles are not all the same colour. I did this by using the mouse location to calculate a colour. Let’s add this feature to our project.
If you set your window size to be 500x500 in the setup() method...
size(500,500);
...then you know that your mouse coordinates can’t be bigger than 500 in either direction.
We also know that the three components in a colour can be between 0 and 255, and 255 is half of 500 (you can learn more about colour in Processing here). Based on that, here’s one way we can select a colour based on the mouse coordinates:
color c = color(mouseX / 2.0, mouseY / 2.0, 200);
I chose 200 for the blue component arbitrarily - you can experiment with any sort of numbers you’d like.
We’re almost there! The last bit of functionality is the ability to have multiple circles on the screen. That is, when we click to add a new circle, we don’t want the old ones to disappear until they’ve already reached their maximum radius.
How is this possible with just one circle variable? Should we add more circle variables somehow?
To solve this problem, we’ll make use of lists. We can maintain a list of all the circles that are still growing on the screen. Then, at each frame of the program, we can remove from the list any circles that have reached the maximum radius. When drawing our circles, we’ll draw anything that’s still in the list.
Have a look at the reference page for ArrayList, Processing’s built in list type.
ArrayList<Circle> circleList = new ArrayList();
(Notice my use of <Circle> in the example code above -- it’s not strictly necessary, but it will make using your list a lot easier because Processing will know what kind of object is stored in the list. Ask a TA if you want to know more about this.)
(Use the reference page to help you use ArrayLists.)
Finally, in the draw() method, you will use a for loop to look at each circle in the list. You’ll need the size() and get() methods from the ArrayList to accomplish this.
(Wondering why you loop until one less than the size of the list? It’s because the indexes start at zero instead of 1, so the last index is actually one less than the size.)
If you run the program now, you’ll notice that the circles stop growing when they’ve reached their maximum size, but they don’t disappear. That’s because they’re still in the list, so they are still being drawn, even if their radius isn’t being changed anymore. To fix that, we need to go through our list and remove the circles we don’t want to draw anymore.
The only hitch is that we can’t go through the list forwards like we did with the previous for loop. This is because as we remove items from the list, it gets smaller. The number of laps through the loop was based on the list’s size, which is now changing. By the time we get to the end, the circle that was there might be totally gone!
It turns out that we can get around this problem just by going through the list backwards. Tha is, we can start on the last lap and work our way down to lap 0. That way, even if we remove something from the end of the list, the first item will still be there.
Inside the for loop, use the ArrayList get() method to get the Circle object whose index is the current lap number.
Before the last step, take a moment to think about how having a Circle class was useful once we moved to having multiple circles. Run it by a TA to see if you’ve got the right idea. (Not sure how to approach this challenge? Think about how you’d write the code if you DIDN’T have a Circle class.)
Our very last item is to add sound to the project. I’m going to give you a bit of challenge here and get you to figure this one out on your own.
Don’t worry, I’m not going to hang you out to dry!
There is an example file that comes with Processing that shows you exactly what you need to do.
This is a really great place to go when you want to get ideas on how to accomplish something in Processing.
Minim is the library that allows Processing projects to use sound. We want to do what the LoadSnippet project is doing.
(If you need a sound file, feel free to download the same one I used in the example.) Don’t forget to add the sound file to the project using the Sketch > Add File... menu item.
You’re all done - great job!!
Want an extra challenge? Try writing code to accomplish any of the following: