xxxxxxxxxx
179
// ====================
// Parameters
// ====================
let numRings = 7;
let gap = 50
// ====================
// My Definitions
// ====================
// Global variables
let rings = [];
let polySynth;
// Functions
// To make it easier to assign notes, since I can just pass a value between 0 to 127 (inclusive). I used an AI to quickly write some of this.
function midiNumberToNoteName(midiNumber) {
// Clamp the MIDI number between 0 and 127 (validating input)
midiNumber = max(0, min(midiNumber, 127));
// Define the note names and their corresponding MIDI numbers
const noteNames = [
"A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"
];
// Calculate the note index and octave
const noteIndex = midiNumber % 12; // Get the index for the note
const octave = floor(midiNumber / 12) - 1; // Calculate the octave
// Construct the note name
const noteName = noteNames[noteIndex] + octave;
return noteName;
}
// Classes
class Dot {
constructor(ringRadius, velocity, colour = 255, startingAngle = 0) {
this.ringRadius = ringRadius;
this.velocity = velocity;
this.colour = color(colour);
this.angle = startingAngle;
}
update() {
this.angle = (this.angle + this.velocity) % TWO_PI;
}
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;
}
}
display() {
fill(this.colour);
noStroke();
circle(cos(this.angle) * this.ringRadius, sin(this.angle) * this.ringRadius, 20);
}
}
class Ring {
constructor(radius, velocity, colour = 255, note = 'C4') {
this.radius = radius;
this.velocity = velocity;
this.colour = color(colour);
this.note = note;
this.thickness = 5;
this.dots = []
}
addDot(startingAngle = 0, colour = this.colour) {
this.dots.push(new Dot(this.radius, this.velocity, colour, startingAngle));
}
update() {
for (let dot of this.dots) {
dot.update()
}
}
display() {
let dotHasCollided = false;
for (let dot of this.dots) {
if (dot.hasCollided()) {
dotHasCollided = true;
}
}
if (dotHasCollided) {
strokeWeight(25)
// Play a sound
userStartAudio();
polySynth.play(this.note, 0.5, 0, 1);
} else {
strokeWeight(this.thickness);
}
stroke(this.colour);
noFill();
arc(0, 0, 2*this.radius, 2*this.radius, 0, TWO_PI);
for (let dot of this.dots) {
dot.display();
}
}
}
// =====================
// p5.js's Functions
// =====================
function setup() {
createCanvas(windowWidth, windowHeight);
background(0)
polySynth = new p5.PolySynth(p5.MonoSynth, numRings*2);
// Initialise the rings and dots
for (let i = 0; i < numRings; i++) {
let radius = gap + (width/2 - 2*gap) * i/(numRings-1);
let dotVelocity = TWO_PI * (i+1)/(30*getTargetFrameRate());
colorMode(HSL)
let ringColour = color(i * 360 / numRings, 100, 80);
colorMode(RGB)
rings.push(new Ring(radius, dotVelocity, ringColour, midiNumberToNoteName(3*i+54))); // Still working on getting a nice sound
rings.at(-1).addDot() //.at(-1) returns the last element of an array
}
}
function draw() {
translate(width/2, height/2)
background(0, 8);
fill(255)
strokeWeight(1)
stroke(255)
circle(0, 0, 10)
noFill()
line(-width/2 + gap, 0, width/2 - gap, 0)
for (let ring of rings) {
ring.update();
ring.display();
}
// drawingContext.filter = 'blur(1px)';
}
// Resize the canvas
function windowResized() {
resizeCanvas(windowWidth, windowHeight)
}