CLICK to start and change shape of the polygon. Move the MOUSE left and right to change tempo and direction.
A fork of Cycle of Fifths by Richard Bourne
xxxxxxxxxx
// For the #WCCChallenge, theme: "downward spiral"
// This isn't a spiral, per se, but it's round, and there's a downward progression of notes
// for some shapes and rotations, especially the pentagon (clockwise) and heptagon (counterclockwise)!
// Inspired by the YouTuber AlgoMotion, who makes amazing videos on CS and music:
// https://www.youtube.com/watch?v=V0YH8M6C-VM
let dim;
let pts = 0;
let notenames = ['C', 'G', 'D', 'A', 'E', 'B', 'G♭', 'D♭', 'Ab', 'E♭', 'B♭', 'F'];
let notefreqs = ['C1', 'G1', 'D1', 'A1', 'E1', 'B1', 'F#1', 'C#1', 'G#1', 'D#1', 'A#1', 'F1'];
let scalepos = [0, 7, 2, 9, 4, 11, 6, 1, 8, 3, 10, 5];
let keytypes = [0, 3, 1, 3, 2, 0, 3, 1, 3, 1, 3, 2];
let pnames = ['triangle', 'square', 'pentagon', 'hexagon', 'heptagon', 'octagon', 'nonagon', 'decagon', 'hendecagon', 'dodecagon'];
let nodes = [];
let pluckers = [];
let index = 0;
let aspeed = 0;
let sampler;
let started = false;
let octave = 1;
function preload() {
sampler = new Tone.Sampler({
urls: {
C2: 'CPiano2.wav',
}
}).toDestination();
}
function setup() {
createCanvas(windowHeight*4/3, windowHeight);
angleMode(DEGREES);
dim = 0.8 * height;
colorMode(HSB, 360);
rectMode(CENTER);
makeNodes();
makePluckers();
sampler.volume.value = -20;
}
function mouseClicked() {
if (!started) {
started = true;
nodes[0].glow = 360
sampler.triggerAttack(nodes[0].freq).toDestination();
} else {
pts = (pts + 1) % 10;
makePluckers();
}
}
function draw() {
if (started) aspeed = lerp(aspeed, map(mouseX, width / 4, 3 * width / 4, -0.5, 0.5, true), 0.2);
background(0);
translate(width / 2, height / 2);
stroke(0, 0, 180);
strokeWeight(dim / 100);
noFill();
ellipse(0, 0, dim);
if (started) {
stroke(0, 0, 360, 300);
strokeWeight(dim / 100);
noFill();
polygon(dim / 2.05);
}
for (let n of nodes) {
n.show();
}
fill(0, 0, 360);
textSize(dim / 15);
noStroke();
textAlign(CENTER, CENTER);
if (!started) text('click to start!', 0, dim / 7)
else text(pnames[pts], 0, dim / 7)
for (let p of pluckers) {
for (let n of nodes) {
if ((abs((n.ang - p.ang) % 360) < 0.5 || abs((n.ang - p.ang) % 360) > 359.5) && n.glow < 200) {
n.glow = 360;
p.col = n.col
p.sat = 360
sampler.triggerAttack(n.freq).toDestination();
}
}
}
for (let p of pluckers) {
p.advance();
}
}
function polygon(radius) {
push();
beginShape();
for (let p of pluckers) {
let sx = radius * cos(p.ang);
let sy = radius * sin(p.ang);
vertex(sx, sy);
}
endShape(CLOSE);
for (let p of pluckers) {
let sx = radius * cos(p.ang);
let sy = radius * sin(p.ang);
stroke(p.col, p.sat, 360, 330)
beginShape();
vertex(sx, sy);
vertex(sx, sy);
curveVertex((radius / 1.8) * cos(p.ang + aspeed * 30), (radius / 1.8) * sin(p.ang + aspeed * 30));
vertex(0, 0);
vertex(0, 0);
endShape();
}
pop();
}
function makeNodes() {
for (let i = 0; i < notenames.length; i++) {
let ang = i * 30 + 270;
let name = notenames[i];
let freq = notefreqs[i];
let pos = scalepos[i];
if (scalepos[i] > 4) pos += 1
let col = scalepos[i] * 30;
let ktype = keytypes[scalepos[i]];
let node = new Node(ang, name, freq, col, pos, ktype);
nodes.push(node);
nodes.sort(function(a, b) {
return a.type - b.type;
});
}
}
function makePluckers() {
pluckers = [];
for (let i = 0; i < (3 + pts); i += 1) {
let ang = i * 360 / (3 + pts)
ang -= 105;
ang = ang % 360
let plucker = new Plucker(ang);
pluckers.push(plucker);
}
}