Chris Crowford’s “The Art of Interactive Design”, was certainly an interesting read. He takes a rather more strict approach to interactivity, perhaps due to the blatant misuse of the word on things undeserving to be called interactive, which in turn potentially makes it more useful, especially as the meaning is easier to grasp. By distilling it into 3 clear parts (listening, thinking, and speaking), it makes it easier for designers to assess where their piece could potentially perform better.
Although it seems a bit controversial, to be honest, I quite agree with his definition, but I can easily see how others might not. Many common forms of interactive media are dismissed by him, but he backs it with a point I liked, a distinction between “interactive” and “intense reaction”, which can so often be blurred.
Another point I really liked was “No Trading Off”. Previously, I would’ve assumed that if 2 parts of his interactive design’s defintion were done really well (while it wouldn’t be the same as all 3 being done well), it would come pretty close. However, he claims this is not the case, and it is a logical thing to believe.
Ultimately, I feel like his definition is most relevant to us in helping us better plan the components of our design, ensuring that it “listens”, “thinks”, and “speaks” well. This is something I could really use, as I often spend too much in the “thinking” and “speaking” part, designing something technically incredible, but ultimately, not as interactive.
One off-topic point, I like his style of writing, as it’s less formal / more personal, which makes it more enjoyable to read (particularly things like his explanation on books & films not being interactive, or the questions).
Earlier in class, the professor showed us the Bloom app (by Brian Eno & Peter Chilvers), which I found really calming & elegant, with its simple and easy to understand function and interface. While thinking about it again at home that day, I remembered a few things I saw a while ago, that mainly related to the music that was coming out. One of them was ProjectJDM‘s videos (most notably his famous rainbow pendulum wave) and CodeCraftedPhysics‘s videos (such as this one). So, for this week’s project, I’d thought it been interesting to try and make something similar to the rainbow pendulum wave, also called, a polyrhymtic spiral (polyrhythm, due to the sound structure, and spiral, due to, it well, making spirals).
# Implementation
To start off, I began by just making some points along a line (plain and simple, without any classes for now).
translate(width/2, height/2)
circle(0, 0, 10) // Center circle, filled (just for reference)
let n = 7 // Number of circles
for (let i = 0; i < n; i++) {
circle(width/15 + i*4*width/(10*n), 0, 10) // Outer dots, outlined
}
I’m just drawing a few circles in a straight line in a loop. The spacing is a bit interesting, as width/15 is the offset from the center, and then the formula i*4*width/(10*n) increments the spacing for each index, mainly by dividing the width by the number of dots (I wrote it as a much simpler but slightly longer equation before, but now I can’t undo it 😅. Besides, I ended up redoing spacing anyways).
The logic for the spacing is as follows. We want points along a line, that have a margin from the center and edges of the screen. So we take the width and subtract a margin * 2 from it. Then we divide up the remaining space by the number of points we want to draw.
Then I changed it to using polar coordinates instead. You should be familiar with polar coordinates and some circle stuff by now (you did read my blog last week right 🤨? It explains some of this), which will come in handy as some of the logic is similar to last week’s work. I position the small circles along larger circles (rings) with differing radii, and then we can move them around in a circle, similar to how we drew the line last week. So now the drawing code is
translate(width/2, height/2)
background(0)
fill(255)
noStroke()
circle(0, 0, 10) // Draw the center circle (filled), just for reference
noFill();
stroke(255)
let n = 7 // Number of rings and dots
let angle = TWO_PI // Angle to draw dots at
let r = 0
for (let i = 0; i < n; i++) {
r = width/15 + i*4*width/(10*n) // Radius of each ring
arc(0, 0, 2*r, 2*r, PI, TWO_PI); // Draw the ring
circle(cos(angle) * r, sin(angle) * r, 10) // Draw the outer dots (outlined)
}
And we get:
And we got a pretty close structure already!
I then played around with the spacing a bit, and ended up using a different equation altogether (same concept and method as original, but I didn’t condense it).
Now, I changed the arcs to be nearly full circles instead (wasn’t sure yet, so you’ll see a lot of screenshots with a gap in the right side, that was intentional to help me note something). Also, now that I had some of the basics of the project down, I switched to using Object Oriented Programming, and made a class for the rings and another for the dots, as well as added a simple line to the center of the screen. I could then animate the dots across the screen by changing their angle. Now, instead of assigning them all the same angle (which would result in a straight line of dots traveling along a circle), I made each dot have a different velocity, and then incremented their angle by that velocity, giving us staggered dots!
The cool thing about the velocity, is that I calculated in such a way that I could control exactly when all the dots came back together again (which is just incredibly cool to see and hear).
Now, I wanted to highlight the ring whose dot has touched the line. I first just tried checking whether the dot’s y position was zero, but that didn’t really work, because a dot could easily skip past it based on it’s velocity and starting angle (and in fact, often did). This was a similar issue with using the angle instead. I then gave a bit of margin (so instead of checking whether angle == 2 Π, I checked whether the difference was less than a certain amount. This seemed to work! (ring highlighted in red, for now, and oh, I also added a trail effect, by doing background(0, 8), a trick I love using).
But unfortunately, not really… It spammed a lot.
I could try adjusting the tolerance/margin, but I knew that was a losing battle (as the lower it is, the fewer false triggers I’ll receive, but also the fewer actual triggers, due to missing it, and vice versa).
So then, I came up with a new method. I would calculate the dot’s position before, now, and after one iteration, and check to see when it was closest to the line. If it was closest to the line now, and was further away before, and would be further away later, then this meant we could trigger the collision. It looked something like this:
class Dot {
// ...
hasCollided() {
let differenceBefore = abs(sin(this.angle - this.velocity));
let differenceNow = abs(sin(this.angle));
let differenceAhead = abs(sin(this.angle + this.velocity));
// check if the dot is closest to the line
if (differenceNow < differenceBefore && differenceNow < differenceAhead) {
return true;
} else {
return false;
}
Aaand, it worked!
After that, I added colors to the rings. I wanted a solution that would dynamically scale with the number of dots, and so what I did was divided up hue (360 degrees) by the number of dots, and used that for the HSL colour (so, similar to the dot’s spacing, but for colour) (also, gosh, isn’t HSL so much better?). I added this line to the loop let ringColour = color(i * 360 / numRings, 100, 80) (and oh, changed the spacing to let radius = gap + (width/2 - 2*gap) * i/(numRings-1) 😅, which is much more understandable).
Then I changed the highlight to the ring’s colour, and tried something out. I increased the ring amount to something much higher (I think 21), and got this cool result.
Refining it a bit more (changing the background call’s opacity), and we get an even more beautiful result! (though it’s hard to tell when the dots collide, as it’s now just a continuous line)
## Sound & Additional Thoughts
Then, I wanted to add some sound (you know, like the MAIN reason I even wanted to make this?). This was a challenge (and took a good chunk of time). Not the actual code for the playing the sound, no that was relatively simple, as I just had to create a new PolySynth (since I wanted to play MIDI notes instead of manual hard coded files, to also scale with the number of rings) with polySynth = new p5.PolySynth(), and then played a sound every time a collision happened (with the sound being based on the ring’s index). No no, the hard part was choosing what sounds to play.
I noticed that p5’s PolySynth accepted MIDI names and notes, but whenever I tried using the numbers, it didn’t really sound as it was supposed to (according to the translation tables). So I created a new rough sketch and explored in there. So basically, MIDI notes first have a character (one A, A#, B, C, C#, D, D#, E, F, F#, G, G#) (though keep it mind it usually starts from C, I don’t why p5 choose to start from A, at least from what I heard), and then a number from 0-8. I eventually created a converter that takes a number and outputs a MIDI note (with the slight help of an AI, though just because I was too tired at this time), and, it worked! Or, worked? Or, well, idk.
Ok, so it worked as intended, but it didn’t really sound nice at all, and nothing like the original video (though to be fair, he is a proper skilled artist, but still). Changing around some of the note assignment did help (I then started the notes from A4 (since I realised that you can’t even hear the first few notes, and some sound pretty terrible, so I restricted the range), and went to every 2nd or 3th note instead), but it still didn’t really sound like how I wanted to. I also tried playing with reverb, etc, but I’m probably missing a key component (yea, who could’ve guessed that a good sounding composition probably has more than just playing some notes in ascending order 😅). I could also just upload some files manually and use those (eg. from Hyperplexed’s CodePen (he also made a great video on it), which would make it sound nice, but that wouldn’t scale with the number of rings, and also feels a bit like cheating (idk why I enjoy making things harder for myself 🤷♂️). Well, at least it looks nice… sometimes.
Unfortunately, that about wraps up all the time I had, so we’re gonna have to end it here. As usual, there’s a lot I couldn’t add (like being to drag a slider to control the rings, and click on them to add dots, which was like, the entire reason for decoupling the dots from the rings (making separate classes, etc), so all that work was for nothing…), and even more I couldn’t write on here (especially about the sound), but that’s it for today, and I’ll catchup you next time! (right? You will read my next posts too right? please? 🥺😅)
Anyways, then, after much ado (😂), I present, my Polyrythmic spiral!
# Final Result
Controls:
Umm… nothing. Just listen to the peaceful and serene music… (just kidding, its doesn’t sound very nice yet 🙁 ).
You could however, adjust the number of rings and the gap between them, by opening the sketch and changing the parameters (found at the top).
Technically, you could also change anything in the sketch, if you get into the code (you could even turn it into a something random, like a giraffe. I don’t know why you would, but you could).
At Eyeo2012, Casey Reas gave a pretty interesting talk, titled”Chance Operations”, where he went somewhat deep into the history of computer graphics, and how randomness was used, sometimes for effect, othertimes as a signal or act of rebellion (against the modern, clean/perfectionist work).
It inspired me to think deeper about how I currently do and potentially could use randomness in my work (ok, fine, also since it’s the writing prompt 😅). I think I would primarily use randomness, in order to create something that can be continually enjoyed. We humans get easily bored, and so don’t really enjoy watching the exact same thing over and over again, since it becomes too predictable and boring (idk about Bob though, he binge watches the same episodes again and again…). So, by adding a certain amount of randomness, we can consistently get a unique and interesting result, which is also incredibly helpful for animation and repeated viewings/interactions.
However, this does also prompt the question about how much randomness to use. Clearly, putting randomness in charge of too many aspects can completely break down the desired effect, and often simply results in gibberish. Therefore, I think the ideal amount of randomness would be one that allows the work to be unique and “alive”, but within its bounds, making the work coherent and interesting. The exact amount of randomness used then ultimately depends on the artist and their desired result, but in almost all cases, randomness is constrained in some way (eg. instead of picking a completely random colour, one of the components of HSL (hue, saturation or lightness) could be random, while controlling the other two, allowing for random, yet pleasing and consistent colours).
For my second week’s project, I was thinking about a simple project that embodied loops. Something, which would make someone think about loops, but not too directly obvious. Just thinking about loops, an action that keeps repeating, the first 2 ideas that popped into my head (after a grid of images) were those circle drawing toys (idk what they’re called 🤷♂️), and L-system trees (huh? but that’s recursive! So, we’ll save that for later 😉 ). I guess we’re doing the first one. Remember these toys?
Ohh, a spirograph! That’s what they’re called 😅. Anyways, yes, those things. They reminded me of a loop because the person carries out the action (moving their hand in a circular motion) again and again (although surprisingly, the result is mesmerising).
I wanted to build something similar, but a bit simpler (idk why, but while building this sketch, I initially felt like I was teaching someone about loops 😅).
# Implementation
Let’s start by first drawing 1 line on a circle. More specifically, the endpoints of the line should lie on the circumference of the circle. We can use the handy unit circle and basic trigonometry to find out that we can get the x and y position of a point, by just supplying an angle and radius/length. This might not seem like a better deal, after all, we’re getting 2 (possibly more confusing) variables, for 2 (simple) variables, however, it makes the maths mucchhhh simpler.
As you can see, the formulas are: x = cos(angle) & y = sin(angle) (but note, that angle must usually be in radians, as p5.js’s default angle mode is radians).
translate(width/2, height/2)
a1 = radians(0)
a2 = radians(90)
r = 300
x1 = cos(a1) * r
y1 = sin(a1) * r
x2 = cos(a2) * r
y2 = sin(a2) * r
line(x1, y1, x2, y2)
That gives us the following (I styled the background black, and the line red)
Ok, but how can we tell this is actually on a circle, and not just some random line? Let’s draw a circle outline and check (I used noFill() and stroke(255));
circle(0, 0, r)
Yay! We can clearly see it’s on the line, and also control the angle! We can add a dot to the center, and also control the line using our mouse.
angle = frameCount/2000 + 1000.07 // Just a random nice looking starting point
num = 50
for (let i=0; i<num; i++) {
x1 = cos(angle * (i-1)) * r;
y1 = sin(angle * (i-1)) * r;
x2 = cos(angle * i) * r;
y2 = sin(angle * i) * r;
strokeWeight(1);
stroke(255, 0, 0, 255);
line(x1, y1, x2, y2);
}
Oo, it’s already a bit mesmerising! (certainly enough for me to at least sit by and watch it for several minutes 😅). You’ll notice that the shape keeps changing, and this is due to the angle changing, which we did by tagging its value to number of frames that had passed since the sketch’s start frameCount (just be sure to slow it down a lot (eg. I used 2000), since there are usually 60 frames per second, so it’ll become too crazy!). Setting a variable based on the frame count like this is an incredibly helpful trick to animate the variables over time.
Another helpful trick is to be able to pause the sketch, which can sometimes help you to debug or understand your sketch better. We can achieve this by calling loop() and noLoop() respectively, based on isLooping()‘s output. It’s important to keep in mind that this stops our draw() function from running, so don’t try to restart the loop from within there! Instead, a better approach is to use a key press, which can be achieved like so:
I just used a ternary operator to make it shorter, but it corresponds to:
function keyPressed(event) {
if (event.key == "p") {
if (isLooping()) {
noLoop();
} else {
loop();
}
}
}
Now, it’s up to you on how you wanna style it, and what features you wanna add! For example, here’s how mine now looks like:
The end.
…
## Drawing Context
Ok ok, fine, I’ll explain a bit, but just a BIT more. You see the cool glow effect? How did I do that? (well, I could just say the function I used, but that’s no fun, nor will you learn much).
p5.js exposes something called the rendering context, through a variable, aptly named (can you guess?)… drawingContext! (better luck next time). But what is that, and where does p5 get the value from?
The rendering context, in short, is basically what p5.js uses to actually draw the stuff on screen (part of the canvas API). For example, when write this:
strokeWeight(10);
noFill();
rect(x, y, w, h);
That might get executed as:
drawingContext.lineWidth = 10;
drawingContext.strokeRect(x, y, w, h);
Note: This is just a hypothetical example, and is probably not how p5.js actually works. For starters, drawingContext is exposed by them as you’ll read now.
And where does it get the value from? Well, it’s the “CanvasRenderingContext2D” (for the default 2D sketches), which it gets by calling .getContext('2d') on the canvas element.
“*Grumble grumble*, yada yada, why does this rendering wendering scherending context even matter?”
Well, firstly, rude, but secondly, good question. It’s because, while p5.js does provide easier functions for most things, it doesn’t expose everything that the rendering context has. Remember the glow effect from earlier? Well, it’s actually done through the .shadowBlur and .shadowColor properties, which aren’t exposed.
In addition to making it much easier to draw shadows/glows, there’s also a MUCH faster (hardware accelerated) blur function! (through the .filter property, which supports more than just blur.) This means we can finally use blur in our sketches, without our performance instantly tanking! (though it still has to be used with caution, as it’s still a heavy effect (similar to the shadows), so too much can still cause performance to drop.)
Also, we can easily make gradients! That’s right, linear, radial, conic, you name it, gradient! I’d recommend checking out these 3 videos to learn more:
For my circle, I added 3 layers of lines. The first one is red and has a stroke weight of 5, with a red glow. The next 2 don’t have any glow, and are blue (with a stroke weight of 4) and white (with a stroke weight of 2) respectively. I also changed a few things, like making it spawn and follow the user’s cursor, and the ability to change it’s size. It also reminded me of Dr. Strange’s “circle-magic-thingies” and similar, so I added another circle to lean into that.
So that’s mostly it! Anyways, without further ado, I present, the final result!
I really like how the end result looks like, especially the colours and glow, and how it animates (hope others find it cool too). Although, I wish I could implement some of my other ideas too (and make it look more like the circular spells you see), but unfortunately that’s all I have time for right now, so we’ll have to end it here. Until next time!
The artwork is an interactive experience where users can input words that dynamically appear on the screen. Each word is assigned a color based on its length:
Blue for words longer than 15 characters
Pink for words longer than 10 characters
Purple for words longer than 5 characters
Dark Blue for words with 5 or fewer characters
The words move around the screen using Perlin noise, creating a smooth and visually appealing effect. A button allows users to stop the movement, freezing the words in their current positions.
Color Palette
Functions Used
setup(): Initializes the input field, label, and button.
newText(): Handles the user input, creates a new word object, and assigns it a random position and color based on length.
draw(): Continuously updates the background and moves the words on the screen.
Word class: Defines the properties and methods for each word, including movement and styling.
toggleMovement(): Toggles the movement of the words on and off.
windowResized(): Adjusts the positioning of elements when the window is resized.
CODE
Challenges Encountered
One major challenge was ensuring that words did not overlap when placed on the screen. I initially struggled to implement checks that would prevent new words from colliding with existing ones, especially near the input text box.
Another challenge was optimizing the use of Perlin noise to create a natural movement without making it too erratic.
Improvements for Future Work
In future iterations of this project, I would like to implement a collision detection system that ensures newly placed words do not overlap with existing words or the input text box. This would enhance the overall user experience and visual organization of the artwork.
Norman’s idea of good design can be summarized by two key characteristics: Discoverability, how easy it is to figure out the functions of something, and Understanding, how well the design conveys meaning. The chapter also highlighted a flaw in everyday objects that I have personally experienced. Norman discusses a swinging door that lacks the characteristic of Understanding, trapping his friend inside. Although the door looks fashionable, it fails to fulfill its basic function—opening and closing.
Another example I found interesting is the sink. He mentioned a sink which fails in its design for the drain. He explained the signifier of the sink fails to guide the users to use it properly. I think in most cases, the design of objects also depend on uniformity, how other similar objects are used will signify the usage of an object that performs the same functionality. This explains why the user does not expect pressing on the stopper would drain the sink, or why the door at a new restaurant does not work (is it automatic? slide sideway? push inside?). Norman explained that this stems from the mismatch in the point of view, the engineer is too focused on the technicalities of the device and normally disregard how it would look from the perspective of users figuring it out on the first use. It is even worse when people are usually impatient or not accessible to the design manual.
Additionally, Norman points out the imbalance of factors in bad designs—one may prioritize fashion too much, while another over-engineers the functionality. In real life, I can think of some examples right here on NYUAD’s campus. The most frustrating one is the fridge commonly used in dorm suites. These fridges are built into the lower kitchen cabinets, blending in with the same wooden color as the other cabinets. However, due to the magnetic door and the absence of a handle or rack, it’s hard to open the fridge. At first glance, I couldn’t even identify which door was the fridge. This is a flaw in both Discoverability and signifiers. It seems the design prioritized the aesthetic of the kitchen over the functionality and user experience. In general, I agree with Norman’s principle of a good design – inclusion of all factors is necessary for a balanced product.
Norman’s idea behind how design should be approached is compelling. Indeed, when designing a product, you need to create it with human behavior in mind because humans are going to be the consumers of your product. This might sound obvious, but in reality, engineers do not consider it. This becomes clear when you look at Norman’s examples in his book. Reading through them felt very validating; finally, someone understands the frustration that comes with bad design.
An example of something that frustrates me that Norman didn’t mention is air conditioning. Back at home, we have this AC controller embedded in the wall that has many buttons and symbols, and none of them make sense. There is no signifier to tell what each button does, and the symbols are vague and arbitrary. The only buttons I know how to use are the temperature and fan. So much of the extra effort by the engineers and the cost of the unusable buttons is wasted because of unclear design. The overall design could be improved if there were signifiers to dictate what each button does or streamlining the settings to make it more simple and clean.
Concept: I really liked the example of using generative text to make poems so I decided to apply a similar approach to stories instead. The story randomly generates by pressing the mouse.
Code: The code I was proud of is that I found a way to simplify the example in the lecture notes. I did that by splitting the data once in setup to make things clearer then I used trim so any accidental extra spaces in the data is removed.
function generateStory() {
let character = trim(random(characters));
let action = trim(random(actions));
let object = trim(random(objects));
let setting = trim(random(settings));
Reflection and improvements: I really am proud of how things turned out. I wish I could have added more to the sketch to make it more entertaining. For example, I could replace the ellipse with a book to make it seem like the story is written on it.
After struggling to come up with a concept/ create something I decided to look at p5.js examples to help me. I found this interactive moving circle that uses the map() function. It reminded me of those Aura Circles and so I decided to do that.
This was the Inspo
Sketch
and this is what I created..
Move the mouse around and click
Highlight
I’m happy with the look of the Circle. I think it looks fairly similar to those Aura Circles. I like the effect it gives when it’s moved.
// drawing the outer circle
fill(circleHue, 20, 100);
circle(width/ 2, height/ 2, diameter);
// drawing the inner circle
fill(circleHue, 10, 200); // Lighter color for inner circle
circle(width/ 2, height/ 2, diameter * 0.5);
Reflection
I could’ve added a lot more words, or even the descriptions that is on the Inspo picture I attached.
Maybe I could’ve also added the “Move the mouse around and click” to be part of the sketch itself.
But honestly I struggled a lot with this assignment. I think it was because I wanted to include the most I could of everything we learned so far, make it interactive, etc. while also improving every assignment. That pressure couldn’t make me work the way I wanted to. So I opted to go for something simple.
For this project I decided to connect it to one of my favorite movies, Harry Potter. One of the best scenes in Harry Potter is when they have just arrived to Hogwarts and the are sorted into four houses (Gryffindor, Slytherin, Ravenclaw, and Hufflepuff) by wearing the sorting hat.
So I decided to use the generative text to sort all our names into these four houses randomly.
Highlighted Code:
function mousePressed() {
if (csvLoaded && names.length > 0 && houses.length > 0) {
// Once the mouse is pressed and the CSV is loaded, show the castle
showCastle = true;
// Pick a random name and a random house
let name = names[int(random(names.length))];
let house = houses[int(random(houses.length))];
// Random House picker message
message = name + " is in " + house + " house";
}
}
The code that I am mostly proud of is the randomized picker. So when you press the mouse, the castle and stars appear and the random name picker starts, and when you press the mouse again another name appears.
Sketch:
Future Improvements:
For the future, I want to try to let the name be picked once and a name does not repeat, for example if it chose my name it should no choose my name again.