Lab Tasks
In this lab, we’re going to be making an interface for generative music. We’ll be taking inspiration from an iPhone app called Bloom by Brian Eno and Peter Chilvers. Don’t worry if you’re not familiar with it, the concept is really simple and explained in the tasks below. Again, what you’re doing in this lab is figuring out what things (objects) exist in the world, writing functions to do stuff to those things, and then finally applying those functions to all the things in the world using a loop.
In this week’s lab you will:
- use JavaScript objects to represent the elements of your sketch, and arrays to store all the elements
- use nested iteration (loops) to compare all possible pairs of these elements (and do things when certain conditions are met)
- create a work of interactive sound-art using the p5.sound library
A friendly caution: this lab is hard compared to the others so far. Ask your tutor if you get stuck.
Task 0: a2 reflection
Today’s task 0 is about your assignnment 2 submission and the interactive elements that you designed.
Post a thumbnail of your assignment 2 submission and a link to it on the test server in the assignment 2 thread on the forum. Identify one interactive element in your assignment and discuss how you accomplished your goal. Think of another interactive element that you didn’t include in your assignment (but wish you did) - discuss how this element could improve your artwork.
Task 1: arrays
Before we start working on our Bloom clone, let’s do a bit of revision—this time on arrays, since it’s been a while since they were covered in week 4. It’s okay to skip this section if you’re already comfortable with arrays.
An array is a different type of variable, just like a string ("hello"
), a
number (1.0
), a boolean (true
), or an object. Except, the value it stores
is a list of other values. The syntax for arrays looks like:
var arrayOfNumbers = [100, 24, -2, 18, 106, 42, 1, 8];
var arrayOfStrings = ["hello", "darkness","my", "old", "friend"];
var arrayOfBooleans = [true, false, true, true, false];
Everything in between the [
and matching ]
makes up the elements of the
array. An element is an item inside the list that is an array.
Each element has a position in the array, we call this the index. The first
element has an index of 0
, the second has an index of 1
, etc.
We can access the elements inside an array using an “indexing” syntax.
let groceryList = ["potato", "panko", "capsicum"];
groceryList[0] = "sweet potato"; // change "potato" to "sweet potato"
This indexing syntax uses [
and ]
again, but this time it’s following the
name of the variable and contains the previously mentioned index of the element
you’d like to access.
Can you complete the following if statement? Make it so that it checks if the third score in the array (42) is greater than 50 (ie. it passed).
It’s also common to want to add elements to an array, we do this with the
push
function.
var items = ["keyboard", "mouse", "monitor"];
items.push("headphones");
// items is now ["keyboard", "mouse", "monitor", "headphones"]
Can you fill in the blank so that the following code adds “jeff” to the array?
Task 2: a single bloomer
Say hello to your neighbour and introduce yourself (if you don’t know each other already!) There isn’t going to be much actual pair-work today, but it’ll still be nice to have someone to bounce ideas off of while you’re working!
Clone your fork of the lab repository repo if you haven’t
already, open it in VSCode, navigate to the week-07
folder and start the live server
as usual. In addition, you’ll need to add the p5.Sound library to your sketch
just like in part 4 of the week 6 lab,
and un-comment the lab-sound.js
script tag in your index.html
which contains
the rest of the helper code for this lab.
Remember, un-commenting in HTML means to remove the
<!--
and -->
before and after the <script>
tag respectively!
For this sketch, we’re going to use the same code pattern as the week 6 lab—except to have multiple bloomers on the screen, we’ll use an array to hold our objects. For a review on objects see last weeks lab content, for a review on arrays see the start of this weeks lab.
That means we will describe our bloomers as an array of JavaScript objects. Ask your partner or your tutor if you can’t remember how these work.
A bloomer is an circular entity which:
- when it’s born, it grows (in size) from nothing
- when it reaches maturity, it “blooms” and plays a sound
- after reaching maturity, it stops growing.
Think back to the week 6 lab (wow, such a long time ago!), where we thought about what fields a Pokémon might have. Discuss with your neighbour what some ideas are for what properties you will need for your bloomer object.
Once you’ve got everything open, paste the following into the sketch.js
file:
var bloomers = [];
function setup() {
createCanvas(displayWidth, displayHeight);
// set up the sound stuff
setupSound();
}
function draw() {
background(0);
// map the "drawBloomer" and "updateBloomer" functions over the bloomer array
// although these functions don't do anything yet - you need to write them (see below)
for (var i = 0; i < bloomers.length; i++) {
drawBloomer(bloomers[i]);
updateBloomer(bloomers[i]);
}
}
function mouseReleased() {
// write some code here that pushes a new bloomer to the array
}
function drawBloomer(b) {
// code which draws the bloomer goes in here
// the bloomer object will be passed in as the `b` parameter
}
function updateBloomer(b) {
// all code which updates parameters in the bloom goes in here
// the bloomer object will be passed in as the `b` parameter
// write some code that makes the bloomer grow
// write a condition that makes the bloomer bloom
}
Now, at the top of your sketch.js
there will be
variable declaration for bloomers
(initialised as an empty array). This is
where you’ll store the bloomer objects in your sketch “world”.
var bloomers = [];
In the setup function use the
.push()
method to add a single bloomer object to the array.
function setup() {
// ...
bloomers.push({ /* your bloomer object goes in here - make sure it's got the right properties */ })
}
Task 3: drawing the bloomers
In the draw()
function your will find that there’s a for
loop which calls
two functions inside the loop: drawBloomer()
and updateBloomer(b)
. Remember
that this is a common pattern for just calling one (or more) functions on each
element of the bloomers
array (one at a time). So, for now, this code is just
like calling drawBloomer
on the object we pushed to the array in the last
step.
Scroll to the drawBloomer(b)
function in the sketch.js
file and write some
code which draws the bloomer object as an ellipse. Remember that you can use the
dot syntax to get a property from an object.
I know we’ve harped on about it a lot in labs & lectures, but it’s really important to understand what’s going on here. Conceptually it’s quite simple:
- there’s an array of bloomer objects (the array variable is called
bloomers
) - you’re writing a function which takes a single bloomer object as a
parameter (
drawBloomer(b)
) - when you call
drawBloomer(b)
function inside thefor
loop, that’s how you draw (or do whatever) to all the bloomers in the array
Don’t get confused and try to make your drawBloomer(b)
function draw all the
bloomers—it only has to draw one. Breaking the problem down like this is super
helpful, especially as the things you want to do to each “thing” in the world
become more complex.
You should now be able to see your bloomer in the browser. After that’s working
properly (ask your tutor or lab neighbour if you get stuck) lets update your
drawBloomer(b)
function so that it changes the colour of the bloomer if it
has bloomed (reached maturity). That sounds like a job for an conditional
statement, don’t you think?
Add an if
-statement to the drawBloomer
function which draws the bloomer with a different colour if the bloomer’s hasBloomed
property is true
.
Task 4: updating the bloomers
The second function you need to define is updateBloomer(b)
. This is where you
should modify any parameters in your bloomer objects.
First up, you want to make your bloomer grow.
Discuss with your neighbour / tutor —how many different ways could you represent this notion of “growth” in a p5 sketch?
In the updateBloomer
function, write some code which modifies the bloomer to
make it “grow”.
Next, you need to add a condition to make your bloomer “bloom”. You can just use its size for now and make it more fun a bit later.
Write an if statement in the updateBloomer
function which checks if the
bloomer has not already bloomed and (&&
) if it is larger than 250. If both
of these are true, set the bloomer’s hasBloomed
value to true and call the
playBoop()
function.
The template repo sets up all the necessary infrastructure for playing sounds in
the lab-sound.js
file. Feel free to look at it if you’re interested in seeing
how it works—but don’t worry about understanding it today, just calling the
playBoop()
function will be enough.
Save your code, and check now that your bloomer grows and then plays a sound as the colour changes.
Task 5: all the bloomers!
To make this interactive, we just need to let the user create more bloomers.
We can do this by using the .push()
method again.
In the mouseReleased()
function, make another call to .push()
which adds a
new bloomer to the bloomers
array, but this time set it’s position to mouseX
and mouseY
.
Save your code and check if you can add a new bloomer by clicking on the screen. This should also grow, change colour and make a sound without you doing any additional work! Hooray for well-planned code!
Make sure that your best bloom code is saved to week-07/sketch.js
in your lab repo and pushed to the GitLab server. Check the CI jobs for a tick to see that your repo is working.
Extra Tasks
Break it down!
For this week’s break it down, let’s have a look at what other effects can be achieved with the bloomers code.
In your pair, open up a cocoding session on p5live. Copy and paste the code below in so you can work on it together — try and make it your own, be creative!
Make some different sounds
If you want to dive deeper into generating music with p5 read and edit the
lab-sound.js
file. The template is setup to use the p5.TriOsc()
object so
why not start by changing that to another oscillator. As always you can learn
all about these in the p5
reference.